From 5322bf743bc74b3c23a87fb011457d328549cffe Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Wed, 20 Oct 2021 12:53:48 +0300 Subject: [PATCH 01/74] 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 02/74] 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 03/74] 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 04/74] 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())) } } } From ed2a2a29aff7308dc29f1bd4f4b787a85b54d729 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 23 Oct 2021 19:44:13 +0300 Subject: [PATCH 05/74] Remove stand-alone properties and use specs instead --- .../kotlin/ru/mipt/npm/controls/api/Device.kt | 14 +- .../ru/mipt/npm/controls/base/DeviceAction.kt | 10 - .../ru/mipt/npm/controls/base/DeviceBase.kt | 252 ----------- .../mipt/npm/controls/base/DeviceProperty.kt | 74 ---- .../npm/controls/base/TypedDeviceProperty.kt | 58 --- .../mipt/npm/controls/base/actionDelegates.kt | 58 --- .../controls/base/devicePropertyDelegates.kt | 283 ------------- .../DeviceBySpec.kt => spec/DeviceBase.kt} | 59 ++- .../DevicePropertySpec.kt | 50 ++- .../{properties => spec}/DeviceSpec.kt | 51 ++- .../{properties => spec}/deviceExtensions.kt | 6 +- .../mipt/npm/controls/{base => spec}/misc.kt | 2 +- .../propertySpecDelegates.kt | 60 +-- .../mipt/npm/controls/properties/delegates.kt | 89 ---- .../controls/properties/getDeviceProperty.kt | 10 - .../npm/controls/spec/getDeviceProperty.kt | 10 + .../controls/opcua/client/MiloDeviceBySpec.kt | 4 +- demo/build.gradle.kts | 10 +- .../ru/mipt/npm/controls/demo/DemoDevice.kt | 52 ++- .../pimotionmaster/PiMotionMasterApp.kt | 47 ++- .../pimotionmaster/PiMotionMasterDevice.kt | 395 +++++++++--------- .../pimotionmaster/fxDeviceProperties.kt | 95 +++-- settings.gradle.kts | 4 +- 23 files changed, 489 insertions(+), 1204 deletions(-) delete mode 100644 controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceAction.kt delete mode 100644 controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceBase.kt delete mode 100644 controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceProperty.kt delete mode 100644 controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/TypedDeviceProperty.kt delete mode 100644 controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/actionDelegates.kt delete mode 100644 controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/devicePropertyDelegates.kt rename controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/{properties/DeviceBySpec.kt => spec/DeviceBase.kt} (79%) rename controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/{properties => spec}/DevicePropertySpec.kt (59%) rename controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/{properties => spec}/DeviceSpec.kt (85%) rename controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/{properties => spec}/deviceExtensions.kt (74%) rename controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/{base => spec}/misc.kt (94%) rename controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/{properties => spec}/propertySpecDelegates.kt (80%) delete mode 100644 controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/properties/delegates.kt delete mode 100644 controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/properties/getDeviceProperty.kt create mode 100644 controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/spec/getDeviceProperty.kt diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Device.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Device.kt index 6298657..eeed7f9 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Device.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Device.kt @@ -57,7 +57,7 @@ public interface Device : Closeable, ContextAware, CoroutineScope { /** * A subscription-based [Flow] of [DeviceMessage] provided by device. The flow is guaranteed to be readable - * multiple times + * multiple times. */ public val messageFlow: Flow @@ -67,6 +67,14 @@ public interface Device : Closeable, ContextAware, CoroutineScope { */ public suspend fun execute(action: String, argument: Meta? = null): Meta? + /** + * Initialize the device. This function suspends until the device is finished initialization + */ + public suspend fun open(): Unit = Unit + + /** + * Close and terminate the device. This function does not wait for device to be closed. + */ override fun close() { cancel("The device is closed") } @@ -85,7 +93,7 @@ public suspend fun Device.getOrReadProperty(propertyName: String): Meta = /** * Get a snapshot of logical state of the device * - * TODO currently this + * TODO currently this */ public fun Device.getProperties(): Meta = Meta { for (descriptor in propertyDescriptors) { @@ -97,4 +105,4 @@ public fun Device.getProperties(): Meta = Meta { * Subscribe on property changes for the whole device */ public fun Device.onPropertyChange(callback: suspend PropertyChangedMessage.() -> Unit): Job = - messageFlow.filterIsInstance().onEach(callback).launchIn(this) \ No newline at end of file + messageFlow.filterIsInstance().onEach(callback).launchIn(this) diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceAction.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceAction.kt deleted file mode 100644 index b75b79f..0000000 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceAction.kt +++ /dev/null @@ -1,10 +0,0 @@ -package ru.mipt.npm.controls.base - -import ru.mipt.npm.controls.api.ActionDescriptor -import space.kscience.dataforge.meta.Meta - -public interface DeviceAction { - public val name: String - public val descriptor: ActionDescriptor - public suspend operator fun invoke(arg: Meta? = null): Meta? -} diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceBase.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceBase.kt deleted file mode 100644 index 52d37bf..0000000 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceBase.kt +++ /dev/null @@ -1,252 +0,0 @@ -package ru.mipt.npm.controls.base - -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import ru.mipt.npm.controls.api.* -import space.kscience.dataforge.context.Context -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.misc.DFExperimental -import kotlin.collections.set -import kotlin.coroutines.CoroutineContext - -//TODO move to DataForge-core -@DFExperimental -public data class LogEntry(val content: String, val priority: Int = 0) - - -@OptIn(ExperimentalCoroutinesApi::class) -private open class BasicReadOnlyDeviceProperty( - val device: DeviceBase, - override val name: String, - default: Meta?, - override val descriptor: PropertyDescriptor, - private val getter: suspend (before: Meta?) -> Meta, -) : ReadOnlyDeviceProperty { - - override val scope: CoroutineScope get() = device - - private val state: MutableStateFlow = MutableStateFlow(default) - override val value: Meta? get() = state.value - - override suspend fun invalidate() { - state.value = null - } - - override fun updateLogical(item: Meta) { - state.value = item - scope.launch { - device.sharedMessageFlow.emit( - PropertyChangedMessage( - property = name, - value = item, - ) - ) - } - } - - override suspend fun read(force: Boolean): Meta { - //backup current value - val currentValue = value - return if (force || currentValue == null) { - //all device operations should be run on device context - //propagate error, but do not fail scope - val res = withContext(scope.coroutineContext + SupervisorJob(scope.coroutineContext[Job])) { - getter(currentValue) - } - updateLogical(res) - res - } else { - currentValue - } - } - - override fun flow(): StateFlow = state -} - - -@OptIn(ExperimentalCoroutinesApi::class) -private class BasicDeviceProperty( - device: DeviceBase, - name: String, - default: Meta?, - descriptor: PropertyDescriptor, - getter: suspend (Meta?) -> Meta, - private val setter: suspend (oldValue: Meta?, newValue: Meta) -> Meta?, -) : BasicReadOnlyDeviceProperty(device, name, default, descriptor, getter), DeviceProperty { - - override var value: Meta? - get() = super.value - set(value) { - scope.launch { - if (value == null) { - invalidate() - } else { - write(value) - } - } - } - - private val writeLock = Mutex() - - override suspend fun write(item: Meta) { - writeLock.withLock { - //fast return if value is not changed - if (item == value) return@withLock - val oldValue = value - //all device operations should be run on device context - withContext(scope.coroutineContext + SupervisorJob(scope.coroutineContext[Job])) { - setter(oldValue, item)?.let { - updateLogical(it) - } - } - } - } -} - -/** - * Baseline implementation of [Device] interface - */ -@Suppress("EXPERIMENTAL_API_USAGE") -public abstract class DeviceBase(final override val context: Context) : Device { - - override val coroutineContext: CoroutineContext = - context.coroutineContext + SupervisorJob(context.coroutineContext[Job]) - - private val _properties = HashMap() - public val properties: Map get() = _properties - private val _actions = HashMap() - public val actions: Map get() = _actions - - internal val sharedMessageFlow = MutableSharedFlow() - - override val messageFlow: SharedFlow get() = sharedMessageFlow - private val sharedLogFlow = MutableSharedFlow() - - /** - * The [SharedFlow] of log messages - */ - @DFExperimental - public val logFlow: SharedFlow - get() = sharedLogFlow - - protected suspend fun log(message: String, priority: Int = 0) { - sharedLogFlow.emit(LogEntry(message, priority)) - } - - override val propertyDescriptors: Collection - get() = _properties.values.map { it.descriptor } - - override val actionDescriptors: Collection - get() = _actions.values.map { it.descriptor } - - private fun

registerProperty(name: String, property: P) { - if (_properties.contains(name)) error("Property with name $name already registered") - _properties[name] = property - } - - internal fun registerAction(name: String, action: DeviceAction) { - if (_actions.contains(name)) error("Action with name $name already registered") - _actions[name] = action - } - - override suspend fun readProperty(propertyName: String): Meta = - (_properties[propertyName] ?: error("Property with name $propertyName not defined")).read() - - override fun getProperty(propertyName: String): Meta? = - (_properties[propertyName] ?: error("Property with name $propertyName not defined")).value - - override suspend fun invalidate(propertyName: String) { - (_properties[propertyName] ?: error("Property with name $propertyName not defined")).invalidate() - } - - override suspend fun writeProperty(propertyName: String, value: Meta) { - (_properties[propertyName] as? DeviceProperty ?: error("Property with name $propertyName not defined")).write( - value - ) - } - - override suspend fun execute(action: String, argument: Meta?): Meta? = - (_actions[action] ?: error("Request with name $action not defined")).invoke(argument) - - /** - * Create a bound read-only property with given [getter] - */ - public fun createReadOnlyProperty( - name: String, - default: Meta?, - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - getter: suspend (Meta?) -> Meta, - ): ReadOnlyDeviceProperty { - val property = BasicReadOnlyDeviceProperty( - this, - name, - default, - PropertyDescriptor(name).apply(descriptorBuilder), - getter - ) - registerProperty(name, property) - return property - } - - - /** - * Create a bound mutable property with given [getter] and [setter] - */ - internal fun createMutableProperty( - name: String, - default: Meta?, - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - getter: suspend (Meta?) -> Meta, - setter: suspend (oldValue: Meta?, newValue: Meta) -> Meta?, - ): DeviceProperty { - val property = BasicDeviceProperty( - this, - name, - default, - PropertyDescriptor(name).apply(descriptorBuilder), - getter, - setter - ) - registerProperty(name, property) - return property - } - - /** - * A stand-alone action - */ - private inner class BasicDeviceAction( - override val name: String, - override val descriptor: ActionDescriptor, - private val block: suspend (Meta?) -> Meta?, - ) : DeviceAction { - override suspend fun invoke(arg: Meta?): Meta? = - withContext(coroutineContext) { - block(arg) - } - } - - /** - * Create a new bound action - */ - internal fun createAction( - name: String, - descriptorBuilder: ActionDescriptor.() -> Unit = {}, - block: suspend (Meta?) -> Meta?, - ): DeviceAction { - val action = BasicDeviceAction(name, ActionDescriptor(name).apply(descriptorBuilder), block) - registerAction(name, action) - return action - } - - public companion object { - - } -} - - - diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceProperty.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceProperty.kt deleted file mode 100644 index 5f67acf..0000000 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceProperty.kt +++ /dev/null @@ -1,74 +0,0 @@ -package ru.mipt.npm.controls.base - -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.Flow -import ru.mipt.npm.controls.api.PropertyDescriptor -import space.kscience.dataforge.meta.Meta -import kotlin.time.Duration - -/** - * Read-only device property - */ -public interface ReadOnlyDeviceProperty { - /** - * Property name, should be unique in device - */ - public val name: String - - /** - * Property descriptor - */ - public val descriptor: PropertyDescriptor - - public val scope: CoroutineScope - - /** - * Erase logical value and force re-read from device on next [read] - */ - public suspend fun invalidate() - - /** - * Directly update property logical value and notify listener without writing it to device - */ - public fun updateLogical(item: Meta) - - /** - * Get cached value and return null if value is invalid or not initialized - */ - public val value: Meta? - - /** - * Read value either from cache if cache is valid or directly from physical device. - * If [force], reread from physical state even if the logical state is set. - */ - public suspend fun read(force: Boolean = false): Meta - - /** - * The [Flow] representing future logical states of the property. - * Produces null when the state is invalidated - */ - public fun flow(): Flow -} - - -/** - * Launch recurring force re-read job on a property scope with given [duration] between reads. - */ -public fun ReadOnlyDeviceProperty.readEvery(duration: Duration): Job = scope.launch { - while (isActive) { - read(true) - delay(duration) - } -} - -/** - * A writeable device property with non-suspended write - */ -public interface DeviceProperty : ReadOnlyDeviceProperty { - override var value: Meta? - - /** - * Write value to physical device. Invalidates logical value, but does not update it automatically - */ - public suspend fun write(item: Meta) -} \ No newline at end of file diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/TypedDeviceProperty.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/TypedDeviceProperty.kt deleted file mode 100644 index b783fe2..0000000 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/TypedDeviceProperty.kt +++ /dev/null @@ -1,58 +0,0 @@ -package ru.mipt.npm.controls.base - -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.transformations.MetaConverter - -/** - * A type-safe wrapper on top of read-only property - */ -public open class TypedReadOnlyDeviceProperty( - private val property: ReadOnlyDeviceProperty, - protected val converter: MetaConverter, -) : ReadOnlyDeviceProperty by property { - - public fun updateLogical(obj: T) { - property.updateLogical(converter.objectToMeta(obj)) - } - - public open val typedValue: T? get() = value?.let { converter.metaToObject(it) } - - public suspend fun readTyped(force: Boolean = false): T { - val meta = read(force) - return converter.metaToObject(meta) - ?: error("Meta $meta could not be converted by $converter") - } - - public fun flowTyped(): Flow = flow().map { it?.let { converter.metaToObject(it) } } -} - -/** - * A type-safe wrapper for a read-write device property - */ -public class TypedDeviceProperty( - private val property: DeviceProperty, - converter: MetaConverter, -) : TypedReadOnlyDeviceProperty(property, converter), DeviceProperty { - - override var value: Meta? - get() = property.value - set(arg) { - property.value = arg - } - - public override var typedValue: T? - get() = value?.let { converter.metaToObject(it) } - set(arg) { - property.value = arg?.let { converter.objectToMeta(arg) } - } - - override suspend fun write(item: Meta) { - property.write(item) - } - - public suspend fun write(obj: T) { - property.write(converter.objectToMeta(obj)) - } -} \ No newline at end of file diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/actionDelegates.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/actionDelegates.kt deleted file mode 100644 index 452e5a1..0000000 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/actionDelegates.kt +++ /dev/null @@ -1,58 +0,0 @@ -package ru.mipt.npm.controls.base - -import ru.mipt.npm.controls.api.ActionDescriptor -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MutableMeta -import space.kscience.dataforge.values.Value -import kotlin.properties.PropertyDelegateProvider -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty - - -private fun D.provideAction(): ReadOnlyProperty = - ReadOnlyProperty { _: D, property: KProperty<*> -> - val name = property.name - return@ReadOnlyProperty actions[name]!! - } - -public typealias ActionDelegate = ReadOnlyProperty - -private class ActionProvider( - val owner: D, - val descriptorBuilder: ActionDescriptor.() -> Unit = {}, - val block: suspend (Meta?) -> Meta?, -) : PropertyDelegateProvider { - override operator fun provideDelegate(thisRef: D, property: KProperty<*>): ActionDelegate { - val name = property.name - owner.createAction(name, descriptorBuilder, block) - return owner.provideAction() - } -} - -public fun DeviceBase.requesting( - descriptorBuilder: ActionDescriptor.() -> Unit = {}, - action: suspend (Meta?) -> Meta?, -): PropertyDelegateProvider = ActionProvider(this, descriptorBuilder, action) - -public fun D.requestingValue( - descriptorBuilder: ActionDescriptor.() -> Unit = {}, - action: suspend (Meta?) -> Any?, -): PropertyDelegateProvider = ActionProvider(this, descriptorBuilder) { - val res = action(it) - Meta(Value.of(res)) -} - -public fun D.requestingMeta( - descriptorBuilder: ActionDescriptor.() -> Unit = {}, - action: suspend MutableMeta.(Meta?) -> Unit, -): PropertyDelegateProvider = ActionProvider(this, descriptorBuilder) { - Meta { action(it) } -} - -public fun DeviceBase.acting( - descriptorBuilder: ActionDescriptor.() -> Unit = {}, - action: suspend (Meta?) -> Unit, -): PropertyDelegateProvider = ActionProvider(this, descriptorBuilder) { - action(it) - null -} \ No newline at end of file diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/devicePropertyDelegates.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/devicePropertyDelegates.kt deleted file mode 100644 index 0f47204..0000000 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/devicePropertyDelegates.kt +++ /dev/null @@ -1,283 +0,0 @@ -package ru.mipt.npm.controls.base - -import ru.mipt.npm.controls.api.PropertyDescriptor -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MutableMeta -import space.kscience.dataforge.meta.boolean -import space.kscience.dataforge.meta.double -import space.kscience.dataforge.meta.transformations.MetaConverter -import space.kscience.dataforge.values.Null -import space.kscience.dataforge.values.Value -import space.kscience.dataforge.values.asValue -import kotlin.properties.PropertyDelegateProvider -import kotlin.properties.ReadOnlyProperty -import kotlin.reflect.KProperty - -private fun D.provideProperty(name: String): ReadOnlyProperty = - ReadOnlyProperty { _: D, _: KProperty<*> -> - return@ReadOnlyProperty properties.getValue(name) - } - -private fun D.provideProperty( - name: String, - converter: MetaConverter, -): ReadOnlyProperty> = - ReadOnlyProperty { _: D, _: KProperty<*> -> - return@ReadOnlyProperty TypedReadOnlyDeviceProperty(properties.getValue(name), converter) - } - - -public typealias ReadOnlyPropertyDelegate = ReadOnlyProperty -public typealias TypedReadOnlyPropertyDelegate = ReadOnlyProperty> - -private class ReadOnlyDevicePropertyProvider( - val owner: D, - val default: Meta?, - val descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - private val getter: suspend (Meta?) -> Meta, -) : PropertyDelegateProvider { - - override operator fun provideDelegate(thisRef: D, property: KProperty<*>): ReadOnlyPropertyDelegate { - val name = property.name - owner.createReadOnlyProperty(name, default, descriptorBuilder, getter) - return owner.provideProperty(name) - } -} - -private class TypedReadOnlyDevicePropertyProvider( - val owner: D, - val default: Meta?, - val converter: MetaConverter, - val descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - private val getter: suspend (Meta?) -> Meta, -) : PropertyDelegateProvider> { - - override operator fun provideDelegate(thisRef: D, property: KProperty<*>): TypedReadOnlyPropertyDelegate { - val name = property.name - owner.createReadOnlyProperty(name, default, descriptorBuilder, getter) - return owner.provideProperty(name, converter) - } -} - -public fun DeviceBase.reading( - default: Meta? = null, - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - getter: suspend (Meta?) -> Meta, -): PropertyDelegateProvider = ReadOnlyDevicePropertyProvider( - this, - default, - descriptorBuilder, - getter -) - -public fun DeviceBase.readingValue( - default: Value? = null, - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - getter: suspend () -> Any?, -): PropertyDelegateProvider = ReadOnlyDevicePropertyProvider( - this, - default?.let { Meta(it) }, - descriptorBuilder, - getter = { Meta(Value.of(getter())) } -) - -public fun DeviceBase.readingNumber( - default: Number? = null, - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - getter: suspend () -> Number, -): PropertyDelegateProvider> = TypedReadOnlyDevicePropertyProvider( - this, - default?.let { Meta(it.asValue()) }, - MetaConverter.number, - descriptorBuilder, - getter = { - val number = getter() - Meta(number.asValue()) - } -) - -public fun DeviceBase.readingDouble( - default: Number? = null, - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - getter: suspend () -> Double, -): PropertyDelegateProvider> = TypedReadOnlyDevicePropertyProvider( - this, - default?.let { Meta(it.asValue()) }, - MetaConverter.double, - descriptorBuilder, - getter = { - val number = getter() - Meta(number.asValue()) - } -) - -public fun DeviceBase.readingString( - default: String? = null, - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - getter: suspend () -> String, -): PropertyDelegateProvider> = TypedReadOnlyDevicePropertyProvider( - this, - default?.let { Meta(it.asValue()) }, - MetaConverter.string, - descriptorBuilder, - getter = { - val number = getter() - Meta(number.asValue()) - } -) - -public fun DeviceBase.readingBoolean( - default: Boolean? = null, - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - getter: suspend () -> Boolean, -): PropertyDelegateProvider> = TypedReadOnlyDevicePropertyProvider( - this, - default?.let { Meta(it.asValue()) }, - MetaConverter.boolean, - descriptorBuilder, - getter = { - val boolean = getter() - Meta(boolean.asValue()) - } -) - -public fun DeviceBase.readingMeta( - default: Meta? = null, - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - getter: suspend MutableMeta.() -> Unit, -): PropertyDelegateProvider> = TypedReadOnlyDevicePropertyProvider( - this, - default, - MetaConverter.meta, - descriptorBuilder, - getter = { - Meta { getter() } - } -) - -private fun DeviceBase.provideMutableProperty(name: String): ReadOnlyProperty = - ReadOnlyProperty { _: DeviceBase, _: KProperty<*> -> - return@ReadOnlyProperty properties[name] as DeviceProperty - } - -private fun DeviceBase.provideMutableProperty( - name: String, - converter: MetaConverter, -): ReadOnlyProperty> = - ReadOnlyProperty { _: DeviceBase, _: KProperty<*> -> - return@ReadOnlyProperty TypedDeviceProperty(properties[name] as DeviceProperty, converter) - } - -public typealias PropertyDelegate = ReadOnlyProperty -public typealias TypedPropertyDelegate = ReadOnlyProperty> - -private class DevicePropertyProvider( - val owner: D, - val default: Meta?, - val descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - private val getter: suspend (Meta?) -> Meta, - private val setter: suspend (oldValue: Meta?, newValue: Meta) -> Meta?, -) : PropertyDelegateProvider { - - override operator fun provideDelegate(thisRef: D, property: KProperty<*>): PropertyDelegate { - val name = property.name - owner.createMutableProperty(name, default, descriptorBuilder, getter, setter) - return owner.provideMutableProperty(name) - } -} - -private class TypedDevicePropertyProvider( - val owner: D, - val default: Meta?, - val converter: MetaConverter, - val descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - private val getter: suspend (Meta?) -> Meta, - private val setter: suspend (oldValue: Meta?, newValue: Meta) -> Meta?, -) : PropertyDelegateProvider> { - - override operator fun provideDelegate(thisRef: D, property: KProperty<*>): TypedPropertyDelegate { - val name = property.name - owner.createMutableProperty(name, default, descriptorBuilder, getter, setter) - return owner.provideMutableProperty(name, converter) - } -} - -public fun DeviceBase.writing( - default: Meta? = null, - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - getter: suspend (Meta?) -> Meta, - setter: suspend (oldValue: Meta?, newValue: Meta) -> Meta?, -): PropertyDelegateProvider = DevicePropertyProvider( - this, - default, - descriptorBuilder, - getter, - setter -) - -public fun DeviceBase.writingVirtual( - default: Meta, - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, -): PropertyDelegateProvider = writing( - default, - descriptorBuilder, - getter = { it ?: default }, - setter = { _, newItem -> newItem } -) - -public fun DeviceBase.writingVirtual( - default: Value, - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, -): PropertyDelegateProvider = writing( - Meta(default), - descriptorBuilder, - getter = { it ?: Meta(default) }, - setter = { _, newItem -> newItem } -) - -public fun D.writingDouble( - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - getter: suspend (Double) -> Double, - setter: suspend (oldValue: Double?, newValue: Double) -> Double?, -): PropertyDelegateProvider> { - val innerGetter: suspend (Meta?) -> Meta = { - Meta(getter(it.double ?: Double.NaN).asValue()) - } - - val innerSetter: suspend (oldValue: Meta?, newValue: Meta) -> Meta? = { oldValue, newValue -> - setter(oldValue.double, newValue.double ?: Double.NaN)?.asMeta() - } - - return TypedDevicePropertyProvider( - this, - Meta(Double.NaN.asValue()), - MetaConverter.double, - descriptorBuilder, - innerGetter, - innerSetter - ) -} - -public fun D.writingBoolean( - descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - getter: suspend (Boolean?) -> Boolean, - setter: suspend (oldValue: Boolean?, newValue: Boolean) -> Boolean?, -): PropertyDelegateProvider> { - val innerGetter: suspend (Meta?) -> Meta = { - Meta(getter(it.boolean).asValue()) - } - - val innerSetter: suspend (oldValue: Meta?, newValue: Meta) -> Meta? = { oldValue, newValue -> - setter(oldValue.boolean, newValue.boolean ?: error("Can't convert $newValue to boolean"))?.asValue() - ?.let { Meta(it) } - } - - return TypedDevicePropertyProvider( - this, - Meta(Null), - MetaConverter.boolean, - descriptorBuilder, - innerGetter, - innerSetter - ) -} \ No newline at end of file diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceBySpec.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceBase.kt similarity index 79% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceBySpec.kt rename to controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceBase.kt index c569cc3..dd00a67 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceBySpec.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceBase.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.properties +package ru.mipt.npm.controls.spec import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob @@ -13,24 +13,15 @@ import space.kscience.dataforge.context.Global import space.kscience.dataforge.meta.Meta import kotlin.coroutines.CoroutineContext -/** - * A device generated from specification - * @param D recursive self-type for properties and actions - */ + @OptIn(InternalDeviceAPI::class) -public open class DeviceBySpec>( - public val spec: DeviceSpec, - context: Context = Global, - meta: Meta = Meta.EMPTY +public abstract class DeviceBase>( + override val context: Context = Global, + public val meta: Meta = Meta.EMPTY ) : Device { - override var context: Context = context - internal set - public var meta: Meta = meta - internal set - - public val properties: Map> get() = spec.properties - public val actions: Map> get() = spec.actions + public abstract val properties: Map> //get() = spec.properties + public abstract val actions: Map> //get() = spec.actions override val propertyDescriptors: Collection get() = properties.values.map { it.descriptor } @@ -68,6 +59,13 @@ public open class DeviceBySpec>( } } + /** + * Update logical state using given [spec] and its convertor + */ + protected suspend fun updateLogical(spec: DevicePropertySpec, value: T) { + updateLogical(spec.name, spec.converter.objectToMeta(value)) + } + /** * Force read physical value and push an update if it is changed. It does not matter if logical state is present. * The logical state is updated after read @@ -98,7 +96,7 @@ public open class DeviceBySpec>( } override suspend fun execute(action: String, argument: Meta?): Meta? = - actions[action]?.executeMeta(self, argument) + actions[action]?.executeWithMeta(self, argument) /** * Read typed value and update/push event if needed @@ -123,19 +121,20 @@ public open class DeviceBySpec>( } } - override fun close() { - with(spec) { self.onShutdown() } - super.close() - } + public suspend operator fun DeviceActionSpec.invoke(input: I? = null): O? = execute(self, input) + } -public suspend fun , T : Any> D.read( - propertySpec: DevicePropertySpec -): T = propertySpec.read() - -public fun , T> D.write( - propertySpec: WritableDevicePropertySpec, - value: T -): Job = launch { - propertySpec.write(value) +/** + * A device generated from specification + * @param D recursive self-type for properties and actions + */ +public open class DeviceBySpec>( + public val spec: DeviceSpec, + context: Context = Global, + meta: Meta = Meta.EMPTY +) : DeviceBase(context, meta) { + override val properties: Map> get() = spec.properties + override val actions: Map> get() = spec.actions } + diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DevicePropertySpec.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DevicePropertySpec.kt similarity index 59% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DevicePropertySpec.kt rename to controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DevicePropertySpec.kt index 23ceffb..a6fb9f6 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DevicePropertySpec.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DevicePropertySpec.kt @@ -1,7 +1,14 @@ -package ru.mipt.npm.controls.properties +package ru.mipt.npm.controls.spec +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch import ru.mipt.npm.controls.api.ActionDescriptor import ru.mipt.npm.controls.api.Device +import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.api.PropertyDescriptor import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.transformations.MetaConverter @@ -75,11 +82,48 @@ public interface DeviceActionSpec { public suspend fun execute(device: D, input: I?): O? } -public suspend fun DeviceActionSpec.executeMeta( +public suspend fun DeviceActionSpec.executeWithMeta( device: D, item: Meta? ): Meta? { val arg = item?.let { inputConverter.metaToObject(item) } val res = execute(device, arg) return res?.let { outputConverter.objectToMeta(res) } -} \ No newline at end of file +} + + +public suspend fun , T : Any> D.read( + propertySpec: DevicePropertySpec +): T = propertySpec.read() + +public suspend fun D.read( + propertySpec: DevicePropertySpec +): T = propertySpec.converter.metaToObject(readProperty(propertySpec.name)) + ?: error("Property meta converter returned null") + +public fun D.write( + propertySpec: WritableDevicePropertySpec, + value: T +): Job = launch { + writeProperty(propertySpec.name, propertySpec.converter.objectToMeta(value)) +} + +public fun , T> D.write( + propertySpec: WritableDevicePropertySpec, + value: T +): Job = launch { + propertySpec.write(value) +} + +/** + * A type safe property change listener + */ +public fun Device.onPropertyChange( + spec: DevicePropertySpec, + callback: suspend PropertyChangedMessage.(T?) -> Unit +): Job = messageFlow + .filterIsInstance() + .filter { it.property == spec.name } + .onEach { change -> + change.callback(spec.converter.metaToObject(change.value)) + }.launchIn(this) \ No newline at end of file diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceSpec.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceSpec.kt similarity index 85% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceSpec.kt rename to controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceSpec.kt index 934220f..cba14ec 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/DeviceSpec.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceSpec.kt @@ -1,10 +1,9 @@ -package ru.mipt.npm.controls.properties +package ru.mipt.npm.controls.spec import kotlinx.coroutines.withContext import ru.mipt.npm.controls.api.ActionDescriptor +import ru.mipt.npm.controls.api.Device import ru.mipt.npm.controls.api.PropertyDescriptor -import space.kscience.dataforge.context.Context -import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.transformations.MetaConverter import kotlin.properties.PropertyDelegateProvider @@ -14,9 +13,7 @@ import kotlin.reflect.KProperty import kotlin.reflect.KProperty1 @OptIn(InternalDeviceAPI::class) -public abstract class DeviceSpec>( - private val buildDevice: () -> D -) : Factory { +public abstract class DeviceSpec { private val _properties = HashMap>() public val properties: Map> get() = _properties @@ -75,8 +72,8 @@ public abstract class DeviceSpec>( public fun property( converter: MetaConverter, - name: String? = null, descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, read: suspend D.() -> T ): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> = PropertyDelegateProvider { _: DeviceSpec, property -> @@ -96,8 +93,8 @@ public abstract class DeviceSpec>( public fun property( converter: MetaConverter, - name: String? = null, descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, read: suspend D.() -> T, write: suspend D.(T) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = @@ -129,8 +126,8 @@ public abstract class DeviceSpec>( public fun action( inputConverter: MetaConverter, outputConverter: MetaConverter, - name: String? = null, descriptorBuilder: ActionDescriptor.() -> Unit = {}, + name: String? = null, execute: suspend D.(I?) -> O? ): PropertyDelegateProvider, ReadOnlyProperty, DeviceActionSpec>> = PropertyDelegateProvider { _: DeviceSpec, property -> @@ -153,19 +150,35 @@ public abstract class DeviceSpec>( } /** - * The function is executed right after device initialization is finished + * An action that takes [Meta] and returns [Meta]. No conversions are done */ - public open fun D.onStartup() {} + public fun metaAction( + descriptorBuilder: ActionDescriptor.() -> Unit = {}, + name: String? = null, + execute: suspend D.(Meta?) -> Meta? + ): PropertyDelegateProvider, ReadOnlyProperty, DeviceActionSpec>> = action( + MetaConverter.Companion.meta, + MetaConverter.Companion.meta, + descriptorBuilder, + name + ){ + execute(it) + } /** - * The function is executed before device is shut down + * An action that takes no parameters and returns no values */ - public open fun D.onShutdown() {} - - - override fun invoke(meta: Meta, context: Context): D = buildDevice().apply { - this.context = context - this.meta = meta - onStartup() + public fun unitAction( + descriptorBuilder: ActionDescriptor.() -> Unit = {}, + name: String? = null, + execute: suspend D.() -> Unit + ): PropertyDelegateProvider, ReadOnlyProperty, DeviceActionSpec>> = action( + MetaConverter.Companion.meta, + MetaConverter.Companion.meta, + descriptorBuilder, + name + ){ + execute() + null } } diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/deviceExtensions.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/deviceExtensions.kt similarity index 74% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/deviceExtensions.kt rename to controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/deviceExtensions.kt index 582c8a7..da83fab 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/deviceExtensions.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/deviceExtensions.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.properties +package ru.mipt.npm.controls.spec import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow @@ -14,7 +14,7 @@ import kotlin.time.Duration * * The flow is canceled when the device scope is canceled */ -public fun , R> D.readRecurring(interval: Duration, reader: suspend D.() -> R): Flow = flow { +public fun , R> D.readRecurring(interval: Duration, reader: suspend D.() -> R): Flow = flow { while (isActive) { kotlinx.coroutines.delay(interval) emit(reader()) @@ -24,7 +24,7 @@ public fun , R> D.readRecurring(interval: Duration, reader: /** * Do a recurring task on a device. The task could */ -public fun > D.doRecurring(interval: Duration, task: suspend D.() -> Unit): Job = launch { +public fun > D.doRecurring(interval: Duration, task: suspend D.() -> Unit): Job = launch { while (isActive) { kotlinx.coroutines.delay(interval) task() diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/misc.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/misc.kt similarity index 94% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/misc.kt rename to controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/misc.kt index 111789f..345f453 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/misc.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/misc.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.base +package ru.mipt.npm.controls.spec import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.double diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/propertySpecDelegates.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/propertySpecDelegates.kt similarity index 80% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/propertySpecDelegates.kt rename to controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/propertySpecDelegates.kt index d087505..7ac8039 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/properties/propertySpecDelegates.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/propertySpecDelegates.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.properties +package ru.mipt.npm.controls.spec import ru.mipt.npm.controls.api.PropertyDescriptor import ru.mipt.npm.controls.api.metaDescriptor @@ -10,19 +10,19 @@ import kotlin.properties.ReadOnlyProperty //read only delegates -public fun > DeviceSpec.booleanProperty( - name: String? = null, +public fun > DeviceSpec.booleanProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, read: suspend D.() -> Boolean ): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> = property( MetaConverter.boolean, - name, { metaDescriptor { type(ValueType.BOOLEAN) } descriptorBuilder() }, + name, read ) @@ -35,110 +35,110 @@ private inline fun numberDescriptor( descriptorBuilder() } -public fun > DeviceSpec.numberProperty( +public fun > DeviceSpec.numberProperty( name: String? = null, descriptorBuilder: PropertyDescriptor.() -> Unit = {}, read: suspend D.() -> Number ): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> = property( MetaConverter.number, - name, numberDescriptor(descriptorBuilder), + name, read ) -public fun > DeviceSpec.doubleProperty( - name: String? = null, +public fun > DeviceSpec.doubleProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, read: suspend D.() -> Double ): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> = property( MetaConverter.double, - name, numberDescriptor(descriptorBuilder), + name, read ) -public fun > DeviceSpec.stringProperty( - name: String? = null, +public fun > DeviceSpec.stringProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, read: suspend D.() -> String ): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> = property( MetaConverter.string, - name, { metaDescriptor { type(ValueType.STRING) } descriptorBuilder() }, + name, read ) -public fun > DeviceSpec.metaProperty( - name: String? = null, +public fun > DeviceSpec.metaProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, read: suspend D.() -> Meta ): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> = property( MetaConverter.meta, - name, { metaDescriptor { type(ValueType.STRING) } descriptorBuilder() }, + name, read ) //read-write delegates -public fun > DeviceSpec.booleanProperty( - name: String? = null, +public fun > DeviceSpec.booleanProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, read: suspend D.() -> Boolean, write: suspend D.(Boolean) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = property( MetaConverter.boolean, - name, { metaDescriptor { type(ValueType.BOOLEAN) } descriptorBuilder() }, + name, read, write ) -public fun > DeviceSpec.numberProperty( - name: String? = null, +public fun > DeviceSpec.numberProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, read: suspend D.() -> Number, write: suspend D.(Number) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = - property(MetaConverter.number, name, numberDescriptor(descriptorBuilder), read, write) + property(MetaConverter.number, numberDescriptor(descriptorBuilder), name, read, write) -public fun > DeviceSpec.doubleProperty( - name: String? = null, +public fun > DeviceSpec.doubleProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, read: suspend D.() -> Double, write: suspend D.(Double) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = - property(MetaConverter.double, name, numberDescriptor(descriptorBuilder), read, write) + property(MetaConverter.double, numberDescriptor(descriptorBuilder), name, read, write) -public fun > DeviceSpec.stringProperty( - name: String? = null, +public fun > DeviceSpec.stringProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, read: suspend D.() -> String, write: suspend D.(String) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = - property(MetaConverter.string, name, descriptorBuilder, read, write) + property(MetaConverter.string, descriptorBuilder, name, read, write) -public fun > DeviceSpec.metaProperty( - name: String? = null, +public fun > DeviceSpec.metaProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, read: suspend D.() -> Meta, write: suspend D.(Meta) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = - property(MetaConverter.meta, name, descriptorBuilder, read, write) \ No newline at end of file + property(MetaConverter.meta, descriptorBuilder, name, read, write) \ No newline at end of file diff --git a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/properties/delegates.kt b/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/properties/delegates.kt deleted file mode 100644 index 7def81d..0000000 --- a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/properties/delegates.kt +++ /dev/null @@ -1,89 +0,0 @@ -package ru.mipt.npm.controls.controllers - -import kotlinx.coroutines.runBlocking -import ru.mipt.npm.controls.base.* -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.transformations.MetaConverter -import kotlin.properties.ReadOnlyProperty -import kotlin.properties.ReadWriteProperty -import kotlin.reflect.KProperty -import kotlin.time.Duration - -/** - * Blocking read of the value - */ -public operator fun ReadOnlyDeviceProperty.getValue(thisRef: Any?, property: KProperty<*>): Meta = - runBlocking(scope.coroutineContext) { - read() - } - -public operator fun TypedReadOnlyDeviceProperty.getValue(thisRef: Any?, property: KProperty<*>): T = - runBlocking(scope.coroutineContext) { - readTyped() - } - -public operator fun DeviceProperty.setValue(thisRef: Any?, property: KProperty<*>, value: Meta) { - this.value = value -} - -public operator fun TypedDeviceProperty.setValue(thisRef: Any?, property: KProperty<*>, value: T) { - this.typedValue = value -} - -public fun ReadOnlyDeviceProperty.convert( - metaConverter: MetaConverter, - forceRead: Boolean, -): ReadOnlyProperty { - return ReadOnlyProperty { _, _ -> - runBlocking(scope.coroutineContext) { - val meta = read(forceRead) - metaConverter.metaToObject(meta)?: error("Meta $meta could not be converted by $metaConverter") - } - } -} - -public fun DeviceProperty.convert( - metaConverter: MetaConverter, - forceRead: Boolean, -): ReadWriteProperty { - return object : ReadWriteProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): T = runBlocking(scope.coroutineContext) { - val meta = read(forceRead) - metaConverter.metaToObject(meta)?: error("Meta $meta could not be converted by $metaConverter") - } - - override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { - this@convert.setValue(thisRef, property, value.let { metaConverter.objectToMeta(it) }) - } - } -} - -public fun ReadOnlyDeviceProperty.double(forceRead: Boolean = false): ReadOnlyProperty = - convert(MetaConverter.double, forceRead) - -public fun DeviceProperty.double(forceRead: Boolean = false): ReadWriteProperty = - convert(MetaConverter.double, forceRead) - -public fun ReadOnlyDeviceProperty.int(forceRead: Boolean = false): ReadOnlyProperty = - convert(MetaConverter.int, forceRead) - -public fun DeviceProperty.int(forceRead: Boolean = false): ReadWriteProperty = - convert(MetaConverter.int, forceRead) - -public fun ReadOnlyDeviceProperty.string(forceRead: Boolean = false): ReadOnlyProperty = - convert(MetaConverter.string, forceRead) - -public fun DeviceProperty.string(forceRead: Boolean = false): ReadWriteProperty = - convert(MetaConverter.string, forceRead) - -public fun ReadOnlyDeviceProperty.boolean(forceRead: Boolean = false): ReadOnlyProperty = - convert(MetaConverter.boolean, forceRead) - -public fun DeviceProperty.boolean(forceRead: Boolean = false): ReadWriteProperty = - convert(MetaConverter.boolean, forceRead) - -public fun ReadOnlyDeviceProperty.duration(forceRead: Boolean = false): ReadOnlyProperty = - convert(DurationConverter, forceRead) - -public fun DeviceProperty.duration(forceRead: Boolean = false): ReadWriteProperty = - convert(DurationConverter, forceRead) \ No newline at end of file diff --git a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/properties/getDeviceProperty.kt b/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/properties/getDeviceProperty.kt deleted file mode 100644 index 3be61d6..0000000 --- a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/properties/getDeviceProperty.kt +++ /dev/null @@ -1,10 +0,0 @@ -package ru.mipt.npm.controls.properties - -import kotlinx.coroutines.runBlocking - -/** - * Blocking property get call - */ -public operator fun , T : Any> D.get( - propertySpec: DevicePropertySpec -): T = runBlocking { read(propertySpec) } \ No newline at end of file diff --git a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/spec/getDeviceProperty.kt b/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/spec/getDeviceProperty.kt new file mode 100644 index 0000000..39c92e9 --- /dev/null +++ b/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/spec/getDeviceProperty.kt @@ -0,0 +1,10 @@ +package ru.mipt.npm.controls.spec + +import kotlinx.coroutines.runBlocking + +/** + * Blocking property get call + */ +public operator fun , T : Any> D.get( + propertySpec: DevicePropertySpec +): T = runBlocking { read(propertySpec) } \ No newline at end of file diff --git a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDeviceBySpec.kt b/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDeviceBySpec.kt index 351115c..d76fc75 100644 --- a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDeviceBySpec.kt +++ b/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDeviceBySpec.kt @@ -4,8 +4,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.eclipse.milo.opcua.sdk.client.OpcUaClient import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId -import ru.mipt.npm.controls.properties.DeviceBySpec -import ru.mipt.npm.controls.properties.DeviceSpec +import ru.mipt.npm.controls.spec.DeviceBySpec +import ru.mipt.npm.controls.spec.DeviceSpec import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Global import space.kscience.dataforge.meta.Meta diff --git a/demo/build.gradle.kts b/demo/build.gradle.kts index 0fb18d9..522357e 100644 --- a/demo/build.gradle.kts +++ b/demo/build.gradle.kts @@ -5,7 +5,7 @@ plugins { } -repositories{ +repositories { mavenCentral() jcenter() maven("https://repo.kotlin.link") @@ -15,7 +15,7 @@ repositories{ val ktorVersion: String by rootProject.extra val rsocketVersion: String by rootProject.extra -dependencies{ +dependencies { implementation(projects.controlsCore) //implementation(projects.controlsServer) implementation(projects.magix.magixServer) @@ -34,15 +34,15 @@ dependencies{ tasks.withType().configureEach { kotlinOptions { jvmTarget = "11" - freeCompilerArgs = freeCompilerArgs + "-Xjvm-default=all" + freeCompilerArgs = freeCompilerArgs + listOf("-Xjvm-default=all", "-Xopt-in=kotlin.RequiresOptIn") } } -javafx{ +javafx { version = "14" modules("javafx.controls") } -application{ +application { mainClass.set("ru.mipt.npm.controls.demo.DemoControllerViewKt") } \ No newline at end of file diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt index 7882cbc..be80a40 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt @@ -1,22 +1,47 @@ package ru.mipt.npm.controls.demo import kotlinx.coroutines.launch -import ru.mipt.npm.controls.properties.* +import ru.mipt.npm.controls.api.metaDescriptor +import ru.mipt.npm.controls.spec.* +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.transformations.MetaConverter +import space.kscience.dataforge.values.ValueType import java.time.Instant import kotlin.time.Duration import kotlin.time.ExperimentalTime -class DemoDevice : DeviceBySpec(DemoDevice) { +class DemoDevice(context: Context, meta: Meta) : DeviceBySpec(DemoDevice, context, meta) { private var timeScaleState = 5000.0 private var sinScaleState = 1.0 private var cosScaleState = 1.0 - companion object : DeviceSpec(::DemoDevice) { + @OptIn(ExperimentalTime::class) + override suspend fun open() { + super.open() + launch { + sinScale.read() + cosScale.read() + timeScale.read() + } + doRecurring(Duration.milliseconds(50)) { + coordinates.read() + } + } + + + companion object : DeviceSpec(), Factory { // register virtual properties based on actual object state - val timeScale by property(MetaConverter.double, DemoDevice::timeScaleState) + val timeScale by property(MetaConverter.double, DemoDevice::timeScaleState) { + metaDescriptor { + type(ValueType.NUMBER) + } + info = "Real to virtual time scale" + } + val sinScale by property(MetaConverter.double, DemoDevice::sinScaleState) val cosScale by property(MetaConverter.double, DemoDevice::cosScaleState) @@ -30,7 +55,13 @@ class DemoDevice : DeviceBySpec(DemoDevice) { kotlin.math.cos(time.toEpochMilli().toDouble() / timeScaleState) * sinScaleState } - val coordinates by metaProperty { + val coordinates by metaProperty( + descriptorBuilder = { + metaDescriptor { + value("time", ValueType.NUMBER) + } + } + ) { Meta { val time = Instant.now() "time" put time.toEpochMilli() @@ -46,15 +77,6 @@ class DemoDevice : DeviceBySpec(DemoDevice) { null } - @OptIn(ExperimentalTime::class) - override fun DemoDevice.onStartup() { - launch { - sinScale.read() - cosScale.read() - } - doRecurring(Duration.milliseconds(50)){ - coordinates.read() - } - } + override fun invoke(meta: Meta, context: Context): DemoDevice = DemoDevice(context, meta) } } \ No newline at end of file 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 67d475f..cf66c82 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 @@ -13,6 +13,9 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.launch import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.installing +import ru.mipt.npm.devices.pimotionmaster.PiMotionMasterDevice.Axis.Companion.maxPosition +import ru.mipt.npm.devices.pimotionmaster.PiMotionMasterDevice.Axis.Companion.minPosition +import ru.mipt.npm.devices.pimotionmaster.PiMotionMasterDevice.Axis.Companion.position import space.kscience.dataforge.context.Global import space.kscience.dataforge.context.fetch import tornadofx.* @@ -40,28 +43,30 @@ fun VBox.piMotionMasterAxis( 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) + with(axis) { + val min = minPosition.read() + val max = maxPosition.read() + val positionProperty = fxProperty(position) + val startPosition = position.read() + 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) + slider(min..max) { + isDisable = true + valueProperty().bind(positionProperty) + } } } } @@ -82,7 +87,7 @@ class PiMotionMasterView : View() { private val controller: PiMotionMasterController by inject() val device = controller.motionMaster - private val connectedProperty: ReadOnlyProperty = device.connected.fxProperty(device) + private val connectedProperty: ReadOnlyProperty = device.fxProperty(PiMotionMasterDevice.connected) private val debugServerJobProperty = SimpleObjectProperty() private val debugServerStarted = debugServerJobProperty.booleanBinding { it != null } //private val axisList = FXCollections.observableArrayList>() 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 20c5dd1..f08bea2 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 @@ -13,13 +13,13 @@ import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withTimeout import ru.mipt.npm.controls.api.DeviceHub import ru.mipt.npm.controls.api.PropertyDescriptor -import ru.mipt.npm.controls.base.* -import ru.mipt.npm.controls.controllers.duration import ru.mipt.npm.controls.ports.* +import ru.mipt.npm.controls.spec.* import space.kscience.dataforge.context.* 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 space.kscience.dataforge.names.NameToken import space.kscience.dataforge.values.asValue import kotlin.collections.component1 @@ -29,70 +29,20 @@ import kotlin.time.Duration class PiMotionMasterDevice( context: Context, private val portFactory: PortFactory = KtorTcpPort, -) : DeviceBase(context), DeviceHub { +) : DeviceBySpec(PiMotionMasterDevice, context), DeviceHub { private var port: Port? = null //TODO make proxy work //PortProxy { portFactory(address ?: error("The device is not connected"), context) } - val connected by readingBoolean(false, descriptorBuilder = { - info = "True if the connection address is defined and the device is initialized" - }) { - port != null - } - - - val connect: DeviceAction by acting({ - info = "Connect to specific port and initialize axis" - }) { portSpec -> - //Clear current actions if present - if (port != null) { - disconnect() - } - //Update port - //address = portSpec.node - port = portFactory(portSpec ?: Meta.EMPTY, context) - connected.updateLogical(true) -// connector.open() - //Initialize axes - if (portSpec != null) { - val idn = identity.read() - failIfError { "Can't connect to $portSpec. Error code: $it" } - logger.info { "Connected to $idn on $portSpec" } - val ids = request("SAI?").map { it.trim() } - if (ids != axes.keys.toList()) { - //re-define axes if needed - axes = ids.associateWith { Axis(it) } - } - Meta(ids.map { it.asValue() }.asValue()) - initialize() - failIfError() - } - } - - val disconnect: DeviceAction by acting({ - info = "Disconnect the program from the device if it is connected" - }) { - if (port != null) { - stop() - port?.close() - } - port = null - connected.updateLogical(false) - } - fun disconnect() { runBlocking { disconnect.invoke() } } - val timeout: DeviceProperty by writingVirtual(200.asValue()) { - info = "Timeout" - } - - var timeoutValue: Duration by timeout.duration() + var timeoutValue: Duration = Duration.microseconds(200) /** * Name-friendly accessor for axis @@ -182,166 +132,225 @@ class PiMotionMasterDevice( } } - val initialize: DeviceAction by acting { - send("INI") - } + companion object : DeviceSpec(), Factory { - val identity: ReadOnlyDeviceProperty by readingString { - request("*IDN?").first() - } + override fun invoke(meta: Meta, context: Context): PiMotionMasterDevice = PiMotionMasterDevice(context) - val firmwareVersion: ReadOnlyDeviceProperty by readingString { - request("VER?").first() - } + val connected by booleanProperty(descriptorBuilder = { + info = "True if the connection address is defined and the device is initialized" + }) { + port != null + } - val stop: DeviceAction by acting( - descriptorBuilder = { + + val initialize by unitAction { + send("INI") + } + + val identity by stringProperty { + request("*IDN?").first() + } + + val firmwareVersion by stringProperty { + request("VER?").first() + } + + val stop by unitAction({ info = "Stop all axis" - }, - action = { send("STP") } - ) + }) { + send("STP") + } - inner class Axis(val axisId: String) : DeviceBase(context) { + val connect by metaAction(descriptorBuilder = { + info = "Connect to specific port and initialize axis" + }) { portSpec -> + //Clear current actions if present + if (port != null) { + disconnect() + } + //Update port + //address = portSpec.node + port = portFactory(portSpec ?: Meta.EMPTY, context) + updateLogical(connected, true) +// connector.open() + //Initialize axes + if (portSpec != null) { + val idn = identity.read() + failIfError { "Can't connect to $portSpec. Error code: $it" } + logger.info { "Connected to $idn on $portSpec" } + val ids = request("SAI?").map { it.trim() } + if (ids != axes.keys.toList()) { + //re-define axes if needed + axes = ids.associateWith { Axis(this, it) } + } + Meta(ids.map { it.asValue() }.asValue()) + initialize() + failIfError() + } + null + } + val disconnect by metaAction({ + info = "Disconnect the program from the device if it is connected" + }) { + if (port != null) { + stop() + port?.close() + } + port = null + updateLogical(connected, false) + null + } + + + val timeout by property(MetaConverter.duration, PiMotionMasterDevice::timeoutValue) { + info = "Timeout" + } + } + + + class Axis( + val mm: PiMotionMasterDevice, + val axisId: String + ) : DeviceBySpec(Axis, mm.context) { + + /** + * TODO Move to head device and abstract + */ private suspend fun readAxisBoolean(command: String): Boolean = - requestAndParse(command, axisId)[axisId]?.toIntOrNull() - ?: error("Malformed $command response. Should include integer value for $axisId") != 0 + (mm.requestAndParse(command, axisId)[axisId]?.toIntOrNull() + ?: error("Malformed $command response. Should include integer value for $axisId")) != 0 + /** + * TODO Move to head device and abstract + */ private suspend fun writeAxisBoolean(command: String, value: Boolean): Boolean { val boolean = if (value) { "1" } else { "0" } - send(command, axisId, boolean) - failIfError() + mm.send(command, axisId, boolean) + mm.failIfError() return value } - private fun axisBooleanProperty(command: String, descriptorBuilder: PropertyDescriptor.() -> Unit = {}) = - writingBoolean( - getter = { readAxisBoolean("$command?") }, - setter = { _, newValue -> - writeAxisBoolean(command, newValue) - }, - descriptorBuilder = descriptorBuilder - ) - - private fun axisNumberProperty(command: String, descriptorBuilder: PropertyDescriptor.() -> Unit = {}) = - writingDouble( - getter = { - requestAndParse("$command?", axisId)[axisId]?.toDoubleOrNull() - ?: error("Malformed $command response. Should include float value for $axisId") - }, - setter = { _, newValue -> - send(command, axisId, newValue.toString()) - failIfError() - newValue - }, - descriptorBuilder = descriptorBuilder - ) - - val enabled by axisBooleanProperty("EAX") { - info = "Motor enable state." - } - - val halt: DeviceAction by acting { - send("HLT", axisId) - } - - val targetPosition by axisNumberProperty("MOV") { - info = """ - Sets a new absolute target position for the specified axis. - Servo mode must be switched on for the commanded axis prior to using this command (closed-loop operation). - """.trimIndent() - } - - val onTarget: TypedReadOnlyDeviceProperty by readingBoolean( - descriptorBuilder = { - info = "Queries the on-target state of the specified axis." - }, - getter = { - readAxisBoolean("ONT?") - } - ) - - val reference: ReadOnlyDeviceProperty by readingBoolean( - descriptorBuilder = { - info = "Get Referencing Result" - }, - getter = { - readAxisBoolean("FRF?") - } - ) - - val moveToReference by acting { - send("FRF", axisId) - } - - val minPosition by readingDouble( - descriptorBuilder = { - info = "Minimal position value for the axis" - }, - getter = { - requestAndParse("TMN?", axisId)[axisId]?.toDoubleOrNull() - ?: error("Malformed `TMN?` response. Should include float value for $axisId") - } - ) - - val maxPosition by readingDouble( - descriptorBuilder = { - info = "Maximal position value for the axis" - }, - getter = { - requestAndParse("TMX?", axisId)[axisId]?.toDoubleOrNull() - ?: error("Malformed `TMX?` response. Should include float value for $axisId") - } - ) - - 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." - } - - val closedLoop: TypedDeviceProperty by axisBooleanProperty("SVO") { - info = "Servo closed loop mode" - } - - val velocity: TypedDeviceProperty by axisNumberProperty("VEL") { - info = "Velocity value for closed-loop operation" - } - - val move by acting { - val target = it.double ?: it?.get("target").double ?: error("Unacceptable target value $it") - closedLoop.write(true) - //optionally set velocity - it?.get("velocity").double?.let { v -> - velocity.write(v) - } - targetPosition.write(target) - //read `onTarget` and `position` properties in a cycle until movement is complete - while (!onTarget.readTyped(true)) { - position.read(true) - delay(200) - } - } - suspend fun move(target: Double) { move(target.asMeta()) } - } - companion object : Factory { - override fun invoke(meta: Meta, context: Context): PiMotionMasterDevice = PiMotionMasterDevice(context) + companion object : DeviceSpec() { + + private fun axisBooleanProperty( + command: String, + descriptorBuilder: PropertyDescriptor.() -> Unit = {} + ) = booleanProperty( + read = { + readAxisBoolean("$command?") + }, + write = { + writeAxisBoolean(command, it) + }, + descriptorBuilder = descriptorBuilder + ) + + private fun axisNumberProperty( + command: String, + descriptorBuilder: PropertyDescriptor.() -> Unit = {} + ) = doubleProperty( + read = { + mm.requestAndParse("$command?", axisId)[axisId]?.toDoubleOrNull() + ?: error("Malformed $command response. Should include float value for $axisId") + }, + write = { newValue -> + mm.send(command, axisId, newValue.toString()) + mm.failIfError() + }, + descriptorBuilder = descriptorBuilder + ) + + val enabled by axisBooleanProperty("EAX") { + info = "Motor enable state." + } + + val halt by unitAction { + mm.send("HLT", axisId) + } + + val targetPosition by axisNumberProperty("MOV") { + info = """ + Sets a new absolute target position for the specified axis. + Servo mode must be switched on for the commanded axis prior to using this command (closed-loop operation). + """.trimIndent() + } + + val onTarget by booleanProperty({ + info = "Queries the on-target state of the specified axis." + }) { + readAxisBoolean("ONT?") + } + + val reference by booleanProperty({ + info = "Get Referencing Result" + }) { + readAxisBoolean("FRF?") + } + + val moveToReference by unitAction { + mm.send("FRF", axisId) + } + + val minPosition by doubleProperty({ + info = "Minimal position value for the axis" + }) { + mm.requestAndParse("TMN?", axisId)[axisId]?.toDoubleOrNull() + ?: error("Malformed `TMN?` response. Should include float value for $axisId") + } + + val maxPosition by doubleProperty({ + info = "Maximal position value for the axis" + }) { + mm.requestAndParse("TMX?", axisId)[axisId]?.toDoubleOrNull() + ?: error("Malformed `TMX?` response. Should include float value for $axisId") + } + + val position by doubleProperty({ + info = "The current axis position." + }) { + mm.requestAndParse("POS?", axisId)[axisId]?.toDoubleOrNull() + ?: error("Malformed `POS?` response. Should include float value for $axisId") + } + + val openLoopTarget by axisNumberProperty("OMA") { + info = "Position for open-loop operation." + } + + val closedLoop by axisBooleanProperty("SVO") { + info = "Servo closed loop mode" + } + + val velocity by axisNumberProperty("VEL") { + info = "Velocity value for closed-loop operation" + } + + val move by metaAction { + val target = it.double ?: it?.get("target").double ?: error("Unacceptable target value $it") + closedLoop.write(true) + //optionally set velocity + it?.get("velocity").double?.let { v -> + velocity.write(v) + } + targetPosition.write(target) + //read `onTarget` and `position` properties in a cycle until movement is complete + while (!onTarget.read()) { + position.read() + delay(200) + } + null + } + + } + } } \ No newline at end of file diff --git a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt b/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt index 48ef6d2..db5e60a 100644 --- a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt +++ b/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt @@ -3,58 +3,67 @@ package ru.mipt.npm.devices.pimotionmaster import javafx.beans.property.ObjectPropertyBase import javafx.beans.property.Property import javafx.beans.property.ReadOnlyProperty -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach import ru.mipt.npm.controls.api.Device -import ru.mipt.npm.controls.base.TypedDeviceProperty -import ru.mipt.npm.controls.base.TypedReadOnlyDeviceProperty +import ru.mipt.npm.controls.spec.DevicePropertySpec +import ru.mipt.npm.controls.spec.WritableDevicePropertySpec +import ru.mipt.npm.controls.spec.onPropertyChange +import ru.mipt.npm.controls.spec.write import space.kscience.dataforge.context.info import space.kscience.dataforge.context.logger import tornadofx.* -fun TypedReadOnlyDeviceProperty.fxProperty(ownerDevice: Device?): ReadOnlyProperty = - object : ObjectPropertyBase() { - override fun getBean(): Any? = ownerDevice - override fun getName(): String = this@fxProperty.name +/** + * Bind a FX property to a device property with a given [spec] + */ +fun Device.fxProperty( + spec: DevicePropertySpec +): ReadOnlyProperty = object : ObjectPropertyBase() { + override fun getBean(): Any = this + override fun getName(): String = spec.name - init { - //Read incoming changes - flowTyped().onEach { - if (it != null) { - runLater { + init { + //Read incoming changes + onPropertyChange(spec) { + if (it != null) { + runLater { + try { set(it) + } catch (ex: Throwable) { + logger.info { "Failed to set property $name to $it" } } - } else { - invalidated() } - }.catch { - ownerDevice?.logger?.info { "Failed to set property $name to $it" } - }.launchIn(scope) - } - } - -fun TypedDeviceProperty.fxProperty(ownerDevice: Device?): Property = - object : ObjectPropertyBase() { - override fun getBean(): Any? = ownerDevice - override fun getName(): String = this@fxProperty.name - - init { - //Read incoming changes - flowTyped().onEach { - if (it != null) { - runLater { - set(it) - } - } else { - invalidated() - } - }.catch { - ownerDevice?.logger?.info { "Failed to set property $name to $it" } - }.launchIn(scope) - - onChange { - typedValue = it + } else { + invalidated() + } + } + } +} + +fun D.fxProperty(spec: WritableDevicePropertySpec): Property = + object : ObjectPropertyBase() { + override fun getBean(): Any = this + override fun getName(): String = spec.name + + init { + //Read incoming changes + onPropertyChange(spec) { + if (it != null) { + runLater { + try { + set(it) + } catch (ex: Throwable) { + logger.info { "Failed to set property $name to $it" } + } + } + } else { + invalidated() + } + } + + onChange { newValue -> + if (newValue != null) { + write(spec, newValue) + } } } } diff --git a/settings.gradle.kts b/settings.gradle.kts index cf5e79e..1f0f7a3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,7 +4,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") enableFeaturePreview("VERSION_CATALOGS") pluginManagement { - val toolsVersion = "0.10.4" + val toolsVersion = "0.10.5" repositories { maven("https://repo.kotlin.link") @@ -28,7 +28,7 @@ dependencyResolutionManagement { versionCatalogs { create("npm") { - from("ru.mipt.npm:version-catalog:0.10.4") + from("ru.mipt.npm:version-catalog:0.10.5") } } } From 7b56436983613995db255752e0b7cdc35e156be9 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 23 Oct 2021 20:00:00 +0300 Subject: [PATCH 06/74] Add open to device initializer --- .../kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt | 4 ++++ demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt index 03880ca..23432ea 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt @@ -1,5 +1,6 @@ package ru.mipt.npm.controls.controllers +import kotlinx.coroutines.launch import ru.mipt.npm.controls.api.Device import ru.mipt.npm.controls.api.DeviceHub import space.kscience.dataforge.context.* @@ -38,6 +39,9 @@ public class DeviceManager : AbstractPlugin(), DeviceHub { public fun DeviceManager.install(name: String, factory: Factory, meta: Meta = Meta.EMPTY): D { val device = factory(meta, context) registerDevice(NameToken(name), device) + device.launch { + device.open() + } return device } diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt index be80a40..8674a03 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt @@ -34,6 +34,9 @@ class DemoDevice(context: Context, meta: Meta) : DeviceBySpec(DemoDe companion object : DeviceSpec(), Factory { + + override fun invoke(meta: Meta, context: Context): DemoDevice = DemoDevice(context, meta) + // register virtual properties based on actual object state val timeScale by property(MetaConverter.double, DemoDevice::timeScaleState) { metaDescriptor { @@ -77,6 +80,5 @@ class DemoDevice(context: Context, meta: Meta) : DeviceBySpec(DemoDe null } - override fun invoke(meta: Meta, context: Context): DemoDevice = DemoDevice(context, meta) } } \ No newline at end of file From 77496aedeccd0d3ad72ab0770c71c34f8cdabf26 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Wed, 20 Oct 2021 12:53:48 +0300 Subject: [PATCH 07/74] 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 24c5188c304efff406a3c6ea944728522608b583 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Wed, 20 Oct 2021 16:50:18 +0300 Subject: [PATCH 08/74] 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 bd23ca1129ff0be54cfee438df093a1030a46e7f Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Wed, 20 Oct 2021 16:59:30 +0300 Subject: [PATCH 09/74] 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 14015178aba5e79e1d41b9a715dcc474e399d19a Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Sat, 23 Oct 2021 12:12:19 +0300 Subject: [PATCH 10/74] 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())) } } } From 357fcec4fe0f177a4e409aae875d9f5df8be401b Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Thu, 28 Oct 2021 11:16:19 +0300 Subject: [PATCH 11/74] Fix issues --- .../controls/demo/virtual_car/VirtualCar.kt | 52 ++++++++++--------- .../demo/virtual_car/VirtualCarController.kt | 17 ------ 2 files changed, 27 insertions(+), 42 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 738b3dd..d086403 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 @@ -1,7 +1,9 @@ package ru.mipt.npm.controls.demo.virtual_car import kotlinx.coroutines.launch -import ru.mipt.npm.controls.properties.* +import ru.mipt.npm.controls.spec.* +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.get @@ -12,18 +14,10 @@ import kotlin.time.ExperimentalTime data class Coordinates(val x: Double = 0.0, val y: Double = 0.0) -class VirtualCar : DeviceBySpec(VirtualCar) { +class VirtualCar(context: Context, meta: Meta) : DeviceBySpec(VirtualCar, context, meta) { private var speedState: Coordinates = Coordinates() - private fun updateAndGetSpeed(): Coordinates { - updateSpeedLocationTime() - return this.speedState - } private var locationState: Coordinates = Coordinates() - private fun updateAndGetLocation(): Coordinates { - updateSpeedLocationTime() - return this.locationState - } private var accelerationState: Coordinates = Coordinates() set(value) { @@ -54,6 +48,24 @@ class VirtualCar : DeviceBySpec(VirtualCar) { ) } + @OptIn(ExperimentalTime::class) + override suspend fun open() { + super.open() + launch { + doRecurring(Duration.seconds(1)) { + carProperties.read() + } + } + launch { + doRecurring(Duration.milliseconds(50)) { + updateSpeedLocationTime() + updateLogical(speed, this@VirtualCar.speedState) + updateLogical(acceleration, this@VirtualCar.accelerationState) + updateLogical(location, this@VirtualCar.locationState) + } + } + } + object CoordinatesMetaConverter : MetaConverter { override fun metaToObject(meta: Meta): Coordinates = Coordinates( meta["x"].double ?: 0.0, @@ -66,10 +78,12 @@ class VirtualCar : DeviceBySpec(VirtualCar) { } } - companion object : DeviceSpec(::VirtualCar) { - val speed by property(CoordinatesMetaConverter) { this.updateAndGetSpeed() } + companion object : DeviceSpec(), Factory { + override fun invoke(meta: Meta, context: Context): VirtualCar = VirtualCar(context, meta) - val location by property(CoordinatesMetaConverter) { this.updateAndGetLocation() } + val speed by property(CoordinatesMetaConverter) { this.speedState } + + val location by property(CoordinatesMetaConverter) { this.locationState } val acceleration by property(CoordinatesMetaConverter, VirtualCar::accelerationState) @@ -82,17 +96,5 @@ class VirtualCar : DeviceBySpec(VirtualCar) { "acceleration" put CoordinatesMetaConverter.objectToMeta(read(acceleration)) } } - - @OptIn(ExperimentalTime::class) - override fun VirtualCar.onStartup() { - launch { - speed.read() - acceleration.read() - location.read() - } - 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 index 7be087e..0bfe5df 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 @@ -7,16 +7,11 @@ 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 @@ -27,13 +22,6 @@ 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) @@ -49,16 +37,11 @@ class VirtualCarController : Controller(), ContextAware { //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() From 4dc33c012d33b6f7d4715bb92434fe8d9a87706c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 30 Oct 2021 13:08:45 +0300 Subject: [PATCH 12/74] Refactor car demo. Add meta property. --- .../kotlin/ru/mipt/npm/controls/api/Device.kt | 6 + .../ru/mipt/npm/controls/spec/DeviceBase.kt | 4 +- .../controls/spec/DeviceMetaPropertySpec.kt | 15 ++ .../npm/controls/spec/DevicePropertySpec.kt | 34 ++--- .../ru/mipt/npm/controls/spec/DeviceSpec.kt | 44 ++++-- .../controls/spec/propertySpecDelegates.kt | 10 +- demo/car/build.gradle.kts | 40 +++++ .../mipt/npm/controls/demo/car/VirtualCar.kt | 137 ++++++++++++++++++ .../demo/car}/VirtualCarController.kt | 21 +-- .../ru/mipt/npm/controls/demo/DemoDevice.kt | 6 +- .../controls/demo/virtual_car/VirtualCar.kt | 100 ------------- .../pimotionmaster/PiMotionMasterDevice.kt | 2 +- .../pimotionmaster/fxDeviceProperties.kt | 5 +- settings.gradle.kts | 2 + 14 files changed, 266 insertions(+), 160 deletions(-) create mode 100644 controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceMetaPropertySpec.kt create mode 100644 demo/car/build.gradle.kts create mode 100644 demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt rename demo/{src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car => car/src/main/kotlin/ru/mipt/npm/controls/demo/car}/VirtualCarController.kt (70%) delete mode 100644 demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Device.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Device.kt index eeed7f9..bd85ae2 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Device.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Device.kt @@ -21,6 +21,12 @@ import space.kscience.dataforge.names.Name */ @Type(DEVICE_TARGET) public interface Device : Closeable, ContextAware, CoroutineScope { + + /** + * Initial configuration meta for the device + */ + public val meta: Meta get() = Meta.EMPTY + /** * List of supported property descriptors */ diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceBase.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceBase.kt index dd00a67..8ba0969 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceBase.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceBase.kt @@ -17,7 +17,7 @@ import kotlin.coroutines.CoroutineContext @OptIn(InternalDeviceAPI::class) public abstract class DeviceBase>( override val context: Context = Global, - public val meta: Meta = Meta.EMPTY + override val meta: Meta = Meta.EMPTY ) : Device { public abstract val properties: Map> //get() = spec.properties @@ -130,7 +130,7 @@ public abstract class DeviceBase>( * @param D recursive self-type for properties and actions */ public open class DeviceBySpec>( - public val spec: DeviceSpec, + public val spec: DeviceSpec, context: Context = Global, meta: Meta = Meta.EMPTY ) : DeviceBase(context, meta) { diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceMetaPropertySpec.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceMetaPropertySpec.kt new file mode 100644 index 0000000..9d37ef8 --- /dev/null +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceMetaPropertySpec.kt @@ -0,0 +1,15 @@ +package ru.mipt.npm.controls.spec + +import ru.mipt.npm.controls.api.Device +import ru.mipt.npm.controls.api.PropertyDescriptor +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.transformations.MetaConverter + +internal object DeviceMetaPropertySpec: DevicePropertySpec { + override val descriptor: PropertyDescriptor = PropertyDescriptor("@meta") + + override val converter: MetaConverter = MetaConverter.meta + + @InternalDeviceAPI + override suspend fun read(device: Device): Meta = device.meta +} \ No newline at end of file diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DevicePropertySpec.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DevicePropertySpec.kt index a6fb9f6..10b3b6b 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DevicePropertySpec.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DevicePropertySpec.kt @@ -17,15 +17,10 @@ import space.kscience.dataforge.meta.transformations.MetaConverter /** * This API is internal and should not be used in user code */ -@RequiresOptIn +@RequiresOptIn("This API should not be called outside of Device internals") public annotation class InternalDeviceAPI public interface DevicePropertySpec { - /** - * Property name, should be unique in device - */ - public val name: String - /** * Property descriptor */ @@ -43,6 +38,11 @@ public interface DevicePropertySpec { public suspend fun read(device: D): T } +/** + * Property name, should be unique in device + */ +public val DevicePropertySpec<*, *>.name: String get() = descriptor.name + @OptIn(InternalDeviceAPI::class) public suspend fun DevicePropertySpec.readMeta(device: D): Meta = converter.objectToMeta(read(device)) @@ -62,11 +62,6 @@ public suspend fun WritableDevicePropertySpec.writeMeta(de } public interface DeviceActionSpec { - /** - * Action name, should be unique in device - */ - public val name: String - /** * Action descriptor */ @@ -82,9 +77,14 @@ public interface DeviceActionSpec { public suspend fun execute(device: D, input: I?): O? } +/** + * Action name, should be unique in device + */ +public val DeviceActionSpec<*, *, *>.name: String get() = descriptor.name + public suspend fun DeviceActionSpec.executeWithMeta( device: D, - item: Meta? + item: Meta?, ): Meta? { val arg = item?.let { inputConverter.metaToObject(item) } val res = execute(device, arg) @@ -93,24 +93,24 @@ public suspend fun DeviceActionSpec.executeWithMeta( public suspend fun , T : Any> D.read( - propertySpec: DevicePropertySpec + propertySpec: DevicePropertySpec, ): T = propertySpec.read() public suspend fun D.read( - propertySpec: DevicePropertySpec + propertySpec: DevicePropertySpec, ): T = propertySpec.converter.metaToObject(readProperty(propertySpec.name)) ?: error("Property meta converter returned null") public fun D.write( propertySpec: WritableDevicePropertySpec, - value: T + value: T, ): Job = launch { writeProperty(propertySpec.name, propertySpec.converter.objectToMeta(value)) } public fun , T> D.write( propertySpec: WritableDevicePropertySpec, - value: T + value: T, ): Job = launch { propertySpec.write(value) } @@ -120,7 +120,7 @@ public fun , T> D.write( */ public fun Device.onPropertyChange( spec: DevicePropertySpec, - callback: suspend PropertyChangedMessage.(T?) -> Unit + callback: suspend PropertyChangedMessage.(T?) -> Unit, ): Job = messageFlow .filterIsInstance() .filter { it.property == spec.name } diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceSpec.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceSpec.kt index cba14ec..9bd8ea2 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceSpec.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceSpec.kt @@ -14,7 +14,10 @@ import kotlin.reflect.KProperty1 @OptIn(InternalDeviceAPI::class) public abstract class DeviceSpec { - private val _properties = HashMap>() + //initializing meta property for everyone + private val _properties = hashMapOf>( + DeviceMetaPropertySpec.name to DeviceMetaPropertySpec + ) public val properties: Map> get() = _properties private val _actions = HashMap>() @@ -31,8 +34,7 @@ public abstract class DeviceSpec { descriptorBuilder: PropertyDescriptor.() -> Unit = {} ): DevicePropertySpec { val deviceProperty = object : DevicePropertySpec { - override val name: String = readOnlyProperty.name - override val descriptor: PropertyDescriptor = PropertyDescriptor(this.name).apply(descriptorBuilder) + override val descriptor: PropertyDescriptor = PropertyDescriptor(readOnlyProperty.name).apply(descriptorBuilder) override val converter: MetaConverter = converter override suspend fun read(device: D): T = withContext(device.coroutineContext) { readOnlyProperty.get(device) } @@ -41,15 +43,38 @@ public abstract class DeviceSpec { } public fun property( + converter: MetaConverter, + readOnlyProperty: KProperty1, + descriptorBuilder: PropertyDescriptor.() -> Unit = {} + ): PropertyDelegateProvider, ReadOnlyProperty>> = + PropertyDelegateProvider { _, property -> + val deviceProperty = object : DevicePropertySpec { + override val descriptor: PropertyDescriptor = PropertyDescriptor(property.name).apply { + //TODO add type from converter + writable = true + }.apply(descriptorBuilder) + + override val converter: MetaConverter = converter + + override suspend fun read(device: D): T = withContext(device.coroutineContext) { + readOnlyProperty.get(device) + } + } + registerProperty(deviceProperty) + ReadOnlyProperty { _, _ -> + deviceProperty + } + } + + public fun mutableProperty( converter: MetaConverter, readWriteProperty: KMutableProperty1, descriptorBuilder: PropertyDescriptor.() -> Unit = {} ): PropertyDelegateProvider, ReadOnlyProperty>> = PropertyDelegateProvider { _, property -> val deviceProperty = object : WritableDevicePropertySpec { - override val name: String = property.name - override val descriptor: PropertyDescriptor = PropertyDescriptor(name).apply { + override val descriptor: PropertyDescriptor = PropertyDescriptor(property.name).apply { //TODO add type from converter writable = true }.apply(descriptorBuilder) @@ -79,8 +104,7 @@ public abstract class DeviceSpec { PropertyDelegateProvider { _: DeviceSpec, property -> val propertyName = name ?: property.name val deviceProperty = object : DevicePropertySpec { - override val name: String = propertyName - override val descriptor: PropertyDescriptor = PropertyDescriptor(this.name).apply(descriptorBuilder) + override val descriptor: PropertyDescriptor = PropertyDescriptor(propertyName).apply(descriptorBuilder) override val converter: MetaConverter = converter override suspend fun read(device: D): T = withContext(device.coroutineContext) { device.read() } @@ -91,7 +115,7 @@ public abstract class DeviceSpec { } } - public fun property( + public fun mutableProperty( converter: MetaConverter, descriptorBuilder: PropertyDescriptor.() -> Unit = {}, name: String? = null, @@ -101,8 +125,7 @@ public abstract class DeviceSpec { PropertyDelegateProvider { _: DeviceSpec, property: KProperty<*> -> val propertyName = name ?: property.name val deviceProperty = object : WritableDevicePropertySpec { - override val name: String = propertyName - override val descriptor: PropertyDescriptor = PropertyDescriptor(this.name).apply(descriptorBuilder) + override val descriptor: PropertyDescriptor = PropertyDescriptor(propertyName).apply(descriptorBuilder) override val converter: MetaConverter = converter override suspend fun read(device: D): T = withContext(device.coroutineContext) { device.read() } @@ -133,7 +156,6 @@ public abstract class DeviceSpec { PropertyDelegateProvider { _: DeviceSpec, property -> val actionName = name ?: property.name val deviceAction = object : DeviceActionSpec { - override val name: String = actionName override val descriptor: ActionDescriptor = ActionDescriptor(actionName).apply(descriptorBuilder) override val inputConverter: MetaConverter = inputConverter diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/propertySpecDelegates.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/propertySpecDelegates.kt index 7ac8039..56cd0ae 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/propertySpecDelegates.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/propertySpecDelegates.kt @@ -97,7 +97,7 @@ public fun > DeviceSpec.booleanProperty( read: suspend D.() -> Boolean, write: suspend D.(Boolean) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = - property( + mutableProperty( MetaConverter.boolean, { metaDescriptor { @@ -117,7 +117,7 @@ public fun > DeviceSpec.numberProperty( read: suspend D.() -> Number, write: suspend D.(Number) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = - property(MetaConverter.number, numberDescriptor(descriptorBuilder), name, read, write) + mutableProperty(MetaConverter.number, numberDescriptor(descriptorBuilder), name, read, write) public fun > DeviceSpec.doubleProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, @@ -125,7 +125,7 @@ public fun > DeviceSpec.doubleProperty( read: suspend D.() -> Double, write: suspend D.(Double) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = - property(MetaConverter.double, numberDescriptor(descriptorBuilder), name, read, write) + mutableProperty(MetaConverter.double, numberDescriptor(descriptorBuilder), name, read, write) public fun > DeviceSpec.stringProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, @@ -133,7 +133,7 @@ public fun > DeviceSpec.stringProperty( read: suspend D.() -> String, write: suspend D.(String) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = - property(MetaConverter.string, descriptorBuilder, name, read, write) + mutableProperty(MetaConverter.string, descriptorBuilder, name, read, write) public fun > DeviceSpec.metaProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, @@ -141,4 +141,4 @@ public fun > DeviceSpec.metaProperty( read: suspend D.() -> Meta, write: suspend D.(Meta) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = - property(MetaConverter.meta, descriptorBuilder, name, read, write) \ No newline at end of file + mutableProperty(MetaConverter.meta, descriptorBuilder, name, read, write) \ No newline at end of file diff --git a/demo/car/build.gradle.kts b/demo/car/build.gradle.kts new file mode 100644 index 0000000..1de811f --- /dev/null +++ b/demo/car/build.gradle.kts @@ -0,0 +1,40 @@ +plugins { + kotlin("jvm") + id("org.openjfx.javafxplugin") + application +} + + +repositories { + mavenCentral() + maven("https://repo.kotlin.link") +} + +val ktorVersion: String by rootProject.extra +val rsocketVersion: String by rootProject.extra + +dependencies { + implementation(projects.controlsCore) + implementation(projects.magix.magixApi) + + implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1") + implementation("no.tornado:tornadofx:1.7.20") + implementation("space.kscience:plotlykt-server:0.5.0-dev-1") + implementation("ch.qos.logback:logback-classic:1.2.3") +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "11" + freeCompilerArgs = freeCompilerArgs + listOf("-Xjvm-default=all", "-Xopt-in=kotlin.RequiresOptIn") + } +} + +javafx { + version = "14" + modules("javafx.controls") +} + +//application { +// mainClass.set("ru.mipt.npm.controls.demo.DemoControllerViewKt") +//} \ No newline at end of file diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt new file mode 100644 index 0000000..5df6a1f --- /dev/null +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt @@ -0,0 +1,137 @@ +@file:OptIn(ExperimentalTime::class) + +package ru.mipt.npm.controls.demo.car + +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.datetime.Clock +import kotlinx.datetime.Instant +import ru.mipt.npm.controls.api.Device +import ru.mipt.npm.controls.spec.DeviceBySpec +import ru.mipt.npm.controls.spec.DeviceSpec +import ru.mipt.npm.controls.spec.doRecurring +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.MetaRepr +import space.kscience.dataforge.meta.double +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.transformations.MetaConverter +import kotlin.math.pow +import kotlin.time.Duration +import kotlin.time.ExperimentalTime + +data class Vector2D(var x: Double = 0.0, var y: Double = 0.0) : MetaRepr { + + override fun toMeta(): Meta = objectToMeta(this) + + operator fun div(arg: Double): Vector2D = Vector2D(x / arg, y / arg) + + companion object CoordinatesMetaConverter : MetaConverter { + override fun metaToObject(meta: Meta): Vector2D = Vector2D( + meta["x"].double ?: 0.0, + meta["y"].double ?: 0.0 + ) + + override fun objectToMeta(obj: Vector2D): Meta = Meta { + "x" put obj.x + "y" put obj.y + } + } +} + +interface IVirtualCar : Device { + var speedState: Vector2D + var locationState: Vector2D + var accelerationState: Vector2D +} + +class VirtualCar(context: Context, meta: Meta) : DeviceBySpec(VirtualCar, context, meta), IVirtualCar { + private val timeScale = 1e-3 + + private val mass by meta.double(1000.0) // mass in kilograms + + override var speedState: Vector2D = Vector2D() + + override var locationState: Vector2D = Vector2D() + + override var accelerationState: Vector2D = Vector2D() + set(value) { + update() + field = value + } + + private var timeState: Instant? = null + + private fun update(newTime: Instant = Clock.System.now()) { + //initialize time if it is not initialized + if (timeState == null) { + timeState = newTime + return + } + + val dt: Double = (newTime - (timeState ?: return)).inWholeMilliseconds.toDouble() * timeScale + + locationState.apply { + x += speedState.x * dt + accelerationState.x * dt.pow(2) / 2.0 + y += speedState.y * dt + accelerationState.y * dt.pow(2) / 2.0 + } + + speedState.apply { + x += dt * accelerationState.x + y += dt * accelerationState.y + } + + //TODO apply friction. One can introduce rotation of the cabin and different friction coefficients along the axis + launch { + //update logical states + location.read() + speed.read() + acceleration.read() + } + + } + + public fun applyForce(force: Vector2D, duration: Duration) { + launch { + update() + accelerationState = force / mass + delay(duration) + accelerationState.apply { + x = 0.0 + y = 0.0 + } + update() + } + } + + @OptIn(ExperimentalTime::class) + override suspend fun open() { + super.open() + //initializing the clock + timeState = Clock.System.now() + //starting regular updates + doRecurring(Duration.milliseconds(100)) { + update() + } + } + + companion object : DeviceSpec(), Factory { + override fun invoke(meta: Meta, context: Context): VirtualCar = VirtualCar(context, meta) + + /** + * Read-only speed + */ + val speed by property(Vector2D, IVirtualCar::speedState) + + /** + * Read-only location + */ + val location by property(Vector2D, IVirtualCar::locationState) + + /** + * writable acceleration + */ + val acceleration by mutableProperty(Vector2D, IVirtualCar::accelerationState) + } +} diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt similarity index 70% rename from demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCarController.kt rename to demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 0bfe5df..1d63d49 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -1,27 +1,20 @@ -package ru.mipt.npm.controls.demo.virtual_car +package ru.mipt.npm.controls.demo.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 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.magix.api.MagixEndpoint -import ru.mipt.npm.magix.rsocket.rSocketWithTcp -import ru.mipt.npm.magix.server.startMagixServer +import ru.mipt.npm.controls.demo.car.VirtualCar.Companion.acceleration import space.kscience.dataforge.context.* import tornadofx.* class VirtualCarController : Controller(), ContextAware { var device: VirtualCar? = null - var magixServer: ApplicationEngine? = null override val context = Context("demoDevice") { plugin(DeviceManager) @@ -32,18 +25,11 @@ class VirtualCarController : Controller(), ContextAware { 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) } } fun shutdown() { logger.info { "Shutting down..." } - magixServer?.stop(1000, 5000) - logger.info { "Magix server stopped" } device?.close() logger.info { "Device server stopped" } context.close() @@ -80,7 +66,8 @@ class VirtualCarControllerView : View(title = " Virtual car controller remote") action { controller.device?.run { launch { - acceleration.write(Coordinates(accelerationXProperty.get(), accelerationYProperty.get())) + acceleration.write(Vector2D(accelerationXProperty.get(), + accelerationYProperty.get())) } } } diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt index 8674a03..d4a3912 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt @@ -38,15 +38,15 @@ class DemoDevice(context: Context, meta: Meta) : DeviceBySpec(DemoDe override fun invoke(meta: Meta, context: Context): DemoDevice = DemoDevice(context, meta) // register virtual properties based on actual object state - val timeScale by property(MetaConverter.double, DemoDevice::timeScaleState) { + val timeScale by mutableProperty(MetaConverter.double, DemoDevice::timeScaleState) { metaDescriptor { type(ValueType.NUMBER) } info = "Real to virtual time scale" } - val sinScale by property(MetaConverter.double, DemoDevice::sinScaleState) - val cosScale by property(MetaConverter.double, DemoDevice::cosScaleState) + val sinScale by mutableProperty(MetaConverter.double, DemoDevice::sinScaleState) + val cosScale by mutableProperty(MetaConverter.double, DemoDevice::cosScaleState) val sin by doubleProperty { val time = Instant.now() 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 deleted file mode 100644 index d086403..0000000 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/virtual_car/VirtualCar.kt +++ /dev/null @@ -1,100 +0,0 @@ -package ru.mipt.npm.controls.demo.virtual_car - -import kotlinx.coroutines.launch -import ru.mipt.npm.controls.spec.* -import space.kscience.dataforge.context.Context -import space.kscience.dataforge.context.Factory -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 - -data class Coordinates(val x: Double = 0.0, val y: Double = 0.0) - -class VirtualCar(context: Context, meta: Meta) : DeviceBySpec(VirtualCar, context, meta) { - private var speedState: Coordinates = Coordinates() - - private var locationState: Coordinates = Coordinates() - - private var accelerationState: Coordinates = Coordinates() - set(value) { - updateSpeedLocationTime() - field = value - } - - private var timeState = Instant.now().toEpochMilli().toDouble() - - 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 - this.timeState = now - this.speedState = Coordinates( - previousSpeed.x + timeDifference * currentAcceleration.x * 1e-3, - previousSpeed.y + timeDifference * currentAcceleration.y * 1e-3 - ) - 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 = Coordinates( - previousLocation.x + locationDifference.x, - previousLocation.y + locationDifference.y - ) - } - - @OptIn(ExperimentalTime::class) - override suspend fun open() { - super.open() - launch { - doRecurring(Duration.seconds(1)) { - carProperties.read() - } - } - launch { - doRecurring(Duration.milliseconds(50)) { - updateSpeedLocationTime() - updateLogical(speed, this@VirtualCar.speedState) - updateLogical(acceleration, this@VirtualCar.accelerationState) - updateLogical(location, this@VirtualCar.locationState) - } - } - } - - object CoordinatesMetaConverter : MetaConverter { - override fun metaToObject(meta: Meta): Coordinates = Coordinates( - meta["x"].double ?: 0.0, - meta["y"].double ?: 0.0 - ) - - override fun objectToMeta(obj: Coordinates): Meta = Meta { - "x" put obj.x - "y" put obj.y - } - } - - companion object : DeviceSpec(), Factory { - override fun invoke(meta: Meta, context: Context): VirtualCar = VirtualCar(context, meta) - - val speed by property(CoordinatesMetaConverter) { this.speedState } - - val location by property(CoordinatesMetaConverter) { this.locationState } - - val acceleration by property(CoordinatesMetaConverter, VirtualCar::accelerationState) - - val carProperties by metaProperty { - Meta { - val time = Instant.now() - "time" put time.toEpochMilli() - "speed" put CoordinatesMetaConverter.objectToMeta(read(speed)) - "location" put CoordinatesMetaConverter.objectToMeta(read(location)) - "acceleration" put CoordinatesMetaConverter.objectToMeta(read(acceleration)) - } - } - } -} 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 f08bea2..2f43e62 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 @@ -203,7 +203,7 @@ class PiMotionMasterDevice( } - val timeout by property(MetaConverter.duration, PiMotionMasterDevice::timeoutValue) { + val timeout by mutableProperty(MetaConverter.duration, PiMotionMasterDevice::timeoutValue) { info = "Timeout" } } diff --git a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt b/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt index db5e60a..3da23f3 100644 --- a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt +++ b/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt @@ -4,10 +4,7 @@ import javafx.beans.property.ObjectPropertyBase import javafx.beans.property.Property import javafx.beans.property.ReadOnlyProperty import ru.mipt.npm.controls.api.Device -import ru.mipt.npm.controls.spec.DevicePropertySpec -import ru.mipt.npm.controls.spec.WritableDevicePropertySpec -import ru.mipt.npm.controls.spec.onPropertyChange -import ru.mipt.npm.controls.spec.write +import ru.mipt.npm.controls.spec.* import space.kscience.dataforge.context.info import space.kscience.dataforge.context.logger import tornadofx.* diff --git a/settings.gradle.kts b/settings.gradle.kts index 1f0f7a3..198eaff 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -4,6 +4,7 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") enableFeaturePreview("VERSION_CATALOGS") pluginManagement { + val toolsVersion = "0.10.5" repositories { @@ -40,6 +41,7 @@ include( ":controls-server", ":controls-opcua", ":demo", + ":demo:car", ":magix", ":magix:magix-api", ":magix:magix-server", From 289ff6ffd0d02dbf0f77f021333a5234454a610a Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Tue, 2 Nov 2021 22:21:13 +0300 Subject: [PATCH 13/74] Add magix for debugging and moved DeviceSpec to IVirtualCar --- demo/car/build.gradle.kts | 3 ++ .../mipt/npm/controls/demo/car/IVirtualCar.kt | 27 ++++++++++++++ .../mipt/npm/controls/demo/car/VirtualCar.kt | 35 ++++--------------- .../controls/demo/car/VirtualCarController.kt | 16 ++++++++- 4 files changed, 51 insertions(+), 30 deletions(-) create mode 100644 demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/IVirtualCar.kt diff --git a/demo/car/build.gradle.kts b/demo/car/build.gradle.kts index 1de811f..9242dc7 100644 --- a/demo/car/build.gradle.kts +++ b/demo/car/build.gradle.kts @@ -16,6 +16,9 @@ val rsocketVersion: String by rootProject.extra dependencies { implementation(projects.controlsCore) implementation(projects.magix.magixApi) + implementation(projects.magix.magixServer) + implementation(projects.magix.magixRsocket) + implementation(projects.controlsMagixClient) implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1") implementation("no.tornado:tornadofx:1.7.20") diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/IVirtualCar.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/IVirtualCar.kt new file mode 100644 index 0000000..b7a2a97 --- /dev/null +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/IVirtualCar.kt @@ -0,0 +1,27 @@ +package ru.mipt.npm.controls.demo.car + +import ru.mipt.npm.controls.api.Device +import ru.mipt.npm.controls.spec.DeviceSpec + +interface IVirtualCar : Device { + var speedState: Vector2D + var locationState: Vector2D + var accelerationState: Vector2D + + companion object : DeviceSpec() { + /** + * Read-only speed + */ + val speed by property(Vector2D, IVirtualCar::speedState) + + /** + * Read-only location + */ + val location by property(Vector2D, IVirtualCar::locationState) + + /** + * writable acceleration + */ + val acceleration by mutableProperty(Vector2D, IVirtualCar::accelerationState) + } +} \ No newline at end of file diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt index 5df6a1f..4c04190 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt @@ -6,9 +6,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.datetime.Clock import kotlinx.datetime.Instant -import ru.mipt.npm.controls.api.Device import ru.mipt.npm.controls.spec.DeviceBySpec -import ru.mipt.npm.controls.spec.DeviceSpec import ru.mipt.npm.controls.spec.doRecurring import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory @@ -40,13 +38,7 @@ data class Vector2D(var x: Double = 0.0, var y: Double = 0.0) : MetaRepr { } } -interface IVirtualCar : Device { - var speedState: Vector2D - var locationState: Vector2D - var accelerationState: Vector2D -} - -class VirtualCar(context: Context, meta: Meta) : DeviceBySpec(VirtualCar, context, meta), IVirtualCar { +class VirtualCar(context: Context, meta: Meta) : DeviceBySpec(IVirtualCar, context, meta), IVirtualCar { private val timeScale = 1e-3 private val mass by meta.double(1000.0) // mass in kilograms @@ -85,14 +77,14 @@ class VirtualCar(context: Context, meta: Meta) : DeviceBySpec(Virtua //TODO apply friction. One can introduce rotation of the cabin and different friction coefficients along the axis launch { //update logical states - location.read() - speed.read() - acceleration.read() + IVirtualCar.location.read() + IVirtualCar.speed.read() + IVirtualCar.acceleration.read() } } - public fun applyForce(force: Vector2D, duration: Duration) { + fun applyForce(force: Vector2D, duration: Duration) { launch { update() accelerationState = force / mass @@ -116,22 +108,7 @@ class VirtualCar(context: Context, meta: Meta) : DeviceBySpec(Virtua } } - companion object : DeviceSpec(), Factory { + companion object : Factory { override fun invoke(meta: Meta, context: Context): VirtualCar = VirtualCar(context, meta) - - /** - * Read-only speed - */ - val speed by property(Vector2D, IVirtualCar::speedState) - - /** - * Read-only location - */ - val location by property(Vector2D, IVirtualCar::locationState) - - /** - * writable acceleration - */ - val acceleration by mutableProperty(Vector2D, IVirtualCar::accelerationState) } } diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 1d63d49..5b0f3ff 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -1,20 +1,27 @@ package ru.mipt.npm.controls.demo.car +import io.ktor.server.engine.* 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 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.car.VirtualCar.Companion.acceleration +import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration +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 override val context = Context("demoDevice") { plugin(DeviceManager) @@ -25,11 +32,18 @@ class VirtualCarController : Controller(), ContextAware { 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) } } fun shutdown() { logger.info { "Shutting down..." } + magixServer?.stop(1000, 5000) + logger.info { "Magix server stopped" } device?.close() logger.info { "Device server stopped" } context.close() From dcd496660c1de40c58a7f89f5d82c120008484b6 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Thu, 4 Nov 2021 15:52:49 +0300 Subject: [PATCH 14/74] Implement MagixVirtualCar --- demo/car/build.gradle.kts | 1 + .../npm/controls/demo/car/MagixVirtualCar.kt | 58 +++++++++++++++++++ .../controls/demo/car/VirtualCarController.kt | 16 +++-- .../mipt/npm/magix/api/MagixMessageFilter.kt | 1 - 4 files changed, 70 insertions(+), 6 deletions(-) create mode 100644 demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt diff --git a/demo/car/build.gradle.kts b/demo/car/build.gradle.kts index 9242dc7..29cfb7d 100644 --- a/demo/car/build.gradle.kts +++ b/demo/car/build.gradle.kts @@ -20,6 +20,7 @@ dependencies { implementation(projects.magix.magixRsocket) implementation(projects.controlsMagixClient) + implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1") implementation("no.tornado:tornadofx:1.7.20") implementation("space.kscience:plotlykt-server:0.5.0-dev-1") diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt new file mode 100644 index 0000000..e56ddf5 --- /dev/null +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt @@ -0,0 +1,58 @@ +package ru.mipt.npm.controls.demo.car + +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import ru.mipt.npm.controls.api.DeviceMessage +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.spec.DeviceBySpec +import ru.mipt.npm.controls.spec.doRecurring +import ru.mipt.npm.magix.api.MagixEndpoint +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.names.Name +import kotlin.time.Duration +import kotlin.time.ExperimentalTime + +class MagixVirtualCar(private val magixEndpoint: MagixEndpoint, context: Context, meta: Meta) + : DeviceBySpec(IVirtualCar, context, meta), IVirtualCar { + override var speedState: Vector2D = Vector2D() + override var locationState: Vector2D = Vector2D() + override var accelerationState: Vector2D = Vector2D() + + private suspend fun MagixEndpoint.startMagixVirtualCarUpdate() { + launch { + subscribe().collect { magix -> + (magix.payload as? PropertyChangedMessage)?.let { message -> + if (message.sourceDevice == Name.parse("virtual-car")) { + when (message.property) { + "speed" -> speedState = Vector2D.metaToObject(message.value) + "location" -> locationState = Vector2D.metaToObject(message.value) + "acceleration" -> accelerationState = Vector2D.metaToObject(message.value) + } + } + } + } + } + } + + @OptIn(ExperimentalTime::class) + override suspend fun open() { + super.open() + + launch { + magixEndpoint.startMagixVirtualCarUpdate() + } + + //starting regular updates + doRecurring(Duration.milliseconds(100)) { + IVirtualCar.speed.read() + IVirtualCar.location.read() + IVirtualCar.acceleration.read() + } + } +} + +class MagixVirtualCarFactory(private val magixEndpoint: MagixEndpoint) : Factory { + override fun invoke(meta: Meta, context: Context): MagixVirtualCar = MagixVirtualCar(magixEndpoint, context, meta) +} diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 5b0f3ff..6b16b91 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -14,13 +14,15 @@ import ru.mipt.npm.controls.controllers.install import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.rsocket.rSocketWithTcp +import ru.mipt.npm.magix.rsocket.rSocketWithWebSockets import ru.mipt.npm.magix.server.startMagixServer import space.kscience.dataforge.context.* import tornadofx.* class VirtualCarController : Controller(), ContextAware { - var device: VirtualCar? = null + var virtualCar: VirtualCar? = null + var magixVirtualCar: MagixVirtualCar? = null var magixServer: ApplicationEngine? = null override val context = Context("demoDevice") { @@ -31,9 +33,11 @@ class VirtualCarController : Controller(), ContextAware { fun init() { context.launch { - device = deviceManager.install("virtual-car", VirtualCar) + virtualCar = deviceManager.install("virtual-car", VirtualCar) //starting magix event loop magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) + val magixEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost", DeviceMessage.serializer()) + magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCarFactory(magixEndpoint)) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) deviceManager.connectToMagix(deviceEndpoint) @@ -44,8 +48,10 @@ class VirtualCarController : Controller(), ContextAware { logger.info { "Shutting down..." } magixServer?.stop(1000, 5000) logger.info { "Magix server stopped" } - device?.close() - logger.info { "Device server stopped" } + magixVirtualCar?.close() + logger.info { "Magix virtual car server stopped" } + virtualCar?.close() + logger.info { "Virtual car server stopped" } context.close() } } @@ -78,7 +84,7 @@ class VirtualCarControllerView : View(title = " Virtual car controller remote") button("Submit") { useMaxWidth = true action { - controller.device?.run { + controller.virtualCar?.run { launch { acceleration.write(Vector2D(accelerationXProperty.get(), accelerationYProperty.get())) diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessageFilter.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessageFilter.kt index 2f941c0..6083ac4 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessageFilter.kt +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessageFilter.kt @@ -24,7 +24,6 @@ public fun Flow>.filter(filter: MagixMessageFilter): Flow filter.format?.contains(message.format) ?: true - && filter.origin?.contains(message.origin) ?: true && filter.origin?.contains(message.origin) ?: true && filter.target?.contains(message.target) ?: true } From 039e0d083b096665f5b223ad7ba8fcb463907b29 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Sat, 6 Nov 2021 09:12:19 +0300 Subject: [PATCH 15/74] Refactor MagixVirtualCar.kt --- .../npm/controls/demo/car/MagixVirtualCar.kt | 37 ++++++++----------- .../mipt/npm/controls/demo/car/VirtualCar.kt | 2 +- .../controls/demo/car/VirtualCarController.kt | 3 +- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt index e56ddf5..3d0c97c 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt @@ -4,21 +4,18 @@ import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.spec.DeviceBySpec -import ru.mipt.npm.controls.spec.doRecurring import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.rsocket.rSocketWithWebSockets import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name -import kotlin.time.Duration import kotlin.time.ExperimentalTime -class MagixVirtualCar(private val magixEndpoint: MagixEndpoint, context: Context, meta: Meta) - : DeviceBySpec(IVirtualCar, context, meta), IVirtualCar { - override var speedState: Vector2D = Vector2D() - override var locationState: Vector2D = Vector2D() - override var accelerationState: Vector2D = Vector2D() +class MagixVirtualCar(context: Context, meta: Meta) + : VirtualCar(context, meta) { private suspend fun MagixEndpoint.startMagixVirtualCarUpdate() { launch { @@ -26,9 +23,7 @@ class MagixVirtualCar(private val magixEndpoint: MagixEndpoint, c (magix.payload as? PropertyChangedMessage)?.let { message -> if (message.sourceDevice == Name.parse("virtual-car")) { when (message.property) { - "speed" -> speedState = Vector2D.metaToObject(message.value) - "location" -> locationState = Vector2D.metaToObject(message.value) - "acceleration" -> accelerationState = Vector2D.metaToObject(message.value) + "acceleration" -> IVirtualCar.acceleration.write(Vector2D.metaToObject(message.value)) } } } @@ -38,21 +33,19 @@ class MagixVirtualCar(private val magixEndpoint: MagixEndpoint, c @OptIn(ExperimentalTime::class) override suspend fun open() { - super.open() + super.open() + + val magixEndpoint = MagixEndpoint.rSocketWithWebSockets( + meta["magixServerHost"].string ?: "localhost", + DeviceMessage.serializer() + ) launch { magixEndpoint.startMagixVirtualCarUpdate() } + } - //starting regular updates - doRecurring(Duration.milliseconds(100)) { - IVirtualCar.speed.read() - IVirtualCar.location.read() - IVirtualCar.acceleration.read() - } + companion object : Factory { + override fun invoke(meta: Meta, context: Context): MagixVirtualCar = MagixVirtualCar(context, meta) } } - -class MagixVirtualCarFactory(private val magixEndpoint: MagixEndpoint) : Factory { - override fun invoke(meta: Meta, context: Context): MagixVirtualCar = MagixVirtualCar(magixEndpoint, context, meta) -} diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt index 4c04190..2cfea25 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt @@ -38,7 +38,7 @@ data class Vector2D(var x: Double = 0.0, var y: Double = 0.0) : MetaRepr { } } -class VirtualCar(context: Context, meta: Meta) : DeviceBySpec(IVirtualCar, context, meta), IVirtualCar { +open class VirtualCar(context: Context, meta: Meta) : DeviceBySpec(IVirtualCar, context, meta), IVirtualCar { private val timeScale = 1e-3 private val mass by meta.double(1000.0) // mass in kilograms diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 6b16b91..974c2ac 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -36,8 +36,7 @@ class VirtualCarController : Controller(), ContextAware { virtualCar = deviceManager.install("virtual-car", VirtualCar) //starting magix event loop magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) - val magixEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost", DeviceMessage.serializer()) - magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCarFactory(magixEndpoint)) + magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) deviceManager.connectToMagix(deviceEndpoint) From df6defd6378016ea1c625fe3856a2e57f0b006c8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 13 Nov 2021 13:25:06 +0300 Subject: [PATCH 16/74] add duplicating local filter for RSocketMagixEndpoint --- .../ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt | 3 ++- .../main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt index 42639ab..490ddaf 100644 --- a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt +++ b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt @@ -20,6 +20,7 @@ import kotlinx.serialization.encodeToString import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixMessage import ru.mipt.npm.magix.api.MagixMessageFilter +import ru.mipt.npm.magix.api.filter import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext @@ -38,7 +39,7 @@ public class RSocketMagixEndpoint( val flow = rSocket.requestStream(payload) return flow.map { MagixEndpoint.magixJson.decodeFromString(serializer, it.data.readText()) - }.flowOn(coroutineContext[CoroutineDispatcher]?:Dispatchers.Unconfined) + }.filter(filter).flowOn(coroutineContext[CoroutineDispatcher] ?: Dispatchers.Unconfined) } override suspend fun broadcast(message: MagixMessage) { diff --git a/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt b/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt index d9d6be5..99f209e 100644 --- a/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt +++ b/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt @@ -21,7 +21,7 @@ public class ZmqMagixEndpoint( payloadSerializer: KSerializer, private val pubPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT, private val pullPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, - private val coroutineContext: CoroutineContext = Dispatchers.IO + private val coroutineContext: CoroutineContext = Dispatchers.IO, ) : MagixEndpoint, AutoCloseable { private val zmqContext by lazy { ZContext() } @@ -54,9 +54,8 @@ public class ZmqMagixEndpoint( } } } - }.filter(filter).flowOn( - coroutineContext[CoroutineDispatcher] ?: Dispatchers.IO - ) //should be flown on IO because of blocking calls + }.filter(filter).flowOn(coroutineContext[CoroutineDispatcher] ?: Dispatchers.IO) + //should be flown on IO because of blocking calls } private val publishSocket by lazy { From 817d32a7e942b786b20e7a384f37017e7112abf0 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Mon, 15 Nov 2021 14:40:19 +0300 Subject: [PATCH 17/74] Try to add xodus dependency --- controls-magix-client/build.gradle.kts | 3 ++- .../commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/controls-magix-client/build.gradle.kts b/controls-magix-client/build.gradle.kts index 3d50b56..dd5b7ef 100644 --- a/controls-magix-client/build.gradle.kts +++ b/controls-magix-client/build.gradle.kts @@ -15,7 +15,8 @@ kotlin { dependencies { implementation(project(":magix:magix-rsocket")) implementation(project(":controls-core")) + implementation("org.jetbrains.xodus:xodus-openAPI:1.3.232") } } } -} \ No newline at end of file +} diff --git a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt index 2e92205..cef2225 100644 --- a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt @@ -13,6 +13,7 @@ import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixMessage import space.kscience.dataforge.context.error import space.kscience.dataforge.context.logger +import jetbrains.exodus.entitystore.Entity public const val DATAFORGE_MAGIX_FORMAT: String = "dataforge" From 81a1afa2b71cb1bb2b6529a8b5057072b36e91f0 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Mon, 15 Nov 2021 15:35:51 +0300 Subject: [PATCH 18/74] Add converters for PropertyChangedMessage and add overload for magix connection --- controls-magix-client/build.gradle.kts | 5 ++ .../ru/mipt/npm/controls/client/dfMagix.kt | 1 - .../mipt/npm/controls/client/xodusDfMagix.kt | 61 +++++++++++++++++++ controls-xodus/build.gradle.kts | 13 ++++ .../ru/mipt/npm/controls/xodus/converters.kt | 33 ++++++++++ settings.gradle.kts | 3 +- 6 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt create mode 100644 controls-xodus/build.gradle.kts create mode 100644 controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt diff --git a/controls-magix-client/build.gradle.kts b/controls-magix-client/build.gradle.kts index dd5b7ef..6acc75f 100644 --- a/controls-magix-client/build.gradle.kts +++ b/controls-magix-client/build.gradle.kts @@ -15,7 +15,12 @@ kotlin { dependencies { implementation(project(":magix:magix-rsocket")) implementation(project(":controls-core")) + } + } + jvmMain { + dependencies { implementation("org.jetbrains.xodus:xodus-openAPI:1.3.232") + implementation(project(":controls-xodus")) } } } diff --git a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt index cef2225..2e92205 100644 --- a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt @@ -13,7 +13,6 @@ import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixMessage import space.kscience.dataforge.context.error import space.kscience.dataforge.context.logger -import jetbrains.exodus.entitystore.Entity public const val DATAFORGE_MAGIX_FORMAT: String = "dataforge" diff --git a/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt b/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt new file mode 100644 index 0000000..680b042 --- /dev/null +++ b/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt @@ -0,0 +1,61 @@ +package ru.mipt.npm.controls.client + +import jetbrains.exodus.entitystore.PersistentEntityStore +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import ru.mipt.npm.controls.api.DeviceMessage +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.controllers.DeviceManager +import ru.mipt.npm.controls.controllers.hubMessageFlow +import ru.mipt.npm.controls.controllers.respondHubMessage +import ru.mipt.npm.controls.xodus.toEntity +import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.api.MagixMessage +import space.kscience.dataforge.context.error +import space.kscience.dataforge.context.logger + +/** + * Communicate with server in [Magix format](https://github.com/waltz-controls/rfc/tree/master/1) and dump messages at xodus entity store + */ +public fun DeviceManager.connectToMagix( + endpoint: MagixEndpoint, + endpointID: String = DATAFORGE_MAGIX_FORMAT, + entityStore: PersistentEntityStore +): Job = context.launch { + endpoint.subscribe().onEach { request -> + val responsePayload = respondHubMessage(request.payload) + if (responsePayload != null) { + val response = MagixMessage( + format = DATAFORGE_MAGIX_FORMAT, + id = generateId(request), + parentId = request.id, + origin = endpointID, + payload = responsePayload + ) + + endpoint.broadcast(response) + } + }.catch { error -> + logger.error(error) { "Error while responding to message" } + }.launchIn(this) + + hubMessageFlow(this).onEach { payload -> + val magixMessage = MagixMessage( + format = DATAFORGE_MAGIX_FORMAT, + id = "df[${payload.hashCode()}]", + origin = endpointID, + payload = payload + ) + endpoint.broadcast(magixMessage) + if (payload is PropertyChangedMessage) { + entityStore.executeInTransaction { + magixMessage.toEntity(it) + } + } + }.catch { error -> + logger.error(error) { "Error while sending a message" } + }.launchIn(this) +} diff --git a/controls-xodus/build.gradle.kts b/controls-xodus/build.gradle.kts new file mode 100644 index 0000000..e6dc62b --- /dev/null +++ b/controls-xodus/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("ru.mipt.npm.gradle.jvm") + `maven-publish` +} + + +dependencies { + implementation(projects.controlsCore) + implementation(projects.magix.magixApi) + implementation("org.jetbrains.xodus:xodus-entity-store:1.3.232") + implementation("org.jetbrains.xodus:xodus-environment:1.3.232") + implementation("org.jetbrains.xodus:xodus-vfs:1.3.232") +} \ No newline at end of file diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt new file mode 100644 index 0000000..6333d00 --- /dev/null +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt @@ -0,0 +1,33 @@ +package ru.mipt.npm.controls.xodus + +import jetbrains.exodus.entitystore.Entity +import jetbrains.exodus.entitystore.StoreTransaction +import ru.mipt.npm.controls.api.DeviceMessage +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.magix.api.MagixMessage + +public fun PropertyChangedMessage.toEntity(transaction: StoreTransaction): Entity { + val entity = transaction.newEntity("PropertyChangedMessage") + entity.setProperty("property", property) + entity.setProperty("value", value.toString()) + entity.setProperty("sourceDevice", sourceDevice.toString()) + targetDevice?.let { entity.setProperty("targetDevice", it.toString()) } + comment?.let { entity.setProperty("comment", it) } + time?.let { entity.setProperty("time", it.toEpochMilliseconds()) } + return entity +} + +public fun MagixMessage.toEntity(transaction: StoreTransaction): Entity { + val entity = transaction.newEntity("MagixMessage") + entity.setProperty("format", format) + entity.setProperty("origin", origin) + if (payload is PropertyChangedMessage) { + val payloadEntity = (payload as PropertyChangedMessage).toEntity(transaction) + entity.setLink("payload", payloadEntity) + } + target?.let { entity.setProperty("target", it) } + id?.let { entity.setProperty("id", it) } + parentId?.let { entity.setProperty("parentId", it) } + user?.let { entity.setProperty("user", it.toString()) } + return entity +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 198eaff..2b76cf6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -50,5 +50,6 @@ include( ":magix:magix-zmq", ":magix:magix-demo", ":controls-magix-client", - ":motors" + ":motors", + ":controls-xodus" ) \ No newline at end of file From a1c3902b92d91e5703700a21be7929af5bf4d31e Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Mon, 15 Nov 2021 16:23:35 +0300 Subject: [PATCH 19/74] Add entity store at controller --- .../ru/mipt/npm/controls/xodus/converters.kt | 3 +-- demo/car/build.gradle.kts | 3 +++ .../npm/controls/demo/car/VirtualCarController.kt | 14 ++++++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt index 6333d00..bb9e070 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt @@ -2,7 +2,6 @@ package ru.mipt.npm.controls.xodus import jetbrains.exodus.entitystore.Entity import jetbrains.exodus.entitystore.StoreTransaction -import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.magix.api.MagixMessage @@ -17,7 +16,7 @@ public fun PropertyChangedMessage.toEntity(transaction: StoreTransaction): Entit return entity } -public fun MagixMessage.toEntity(transaction: StoreTransaction): Entity { +public fun MagixMessage.toEntity(transaction: StoreTransaction): Entity { val entity = transaction.newEntity("MagixMessage") entity.setProperty("format", format) entity.setProperty("origin", origin) diff --git a/demo/car/build.gradle.kts b/demo/car/build.gradle.kts index 29cfb7d..c5b3da5 100644 --- a/demo/car/build.gradle.kts +++ b/demo/car/build.gradle.kts @@ -25,6 +25,9 @@ dependencies { implementation("no.tornado:tornadofx:1.7.20") implementation("space.kscience:plotlykt-server:0.5.0-dev-1") implementation("ch.qos.logback:logback-classic:1.2.3") + implementation("org.jetbrains.xodus:xodus-entity-store:1.3.232") + implementation("org.jetbrains.xodus:xodus-environment:1.3.232") + implementation("org.jetbrains.xodus:xodus-vfs:1.3.232") } tasks.withType().configureEach { diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 974c2ac..a8f5e54 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -6,6 +6,9 @@ import javafx.scene.Parent import javafx.scene.control.TextField import javafx.scene.layout.Priority import javafx.stage.Stage +import jetbrains.exodus.entitystore.PersistentEntityStore +import jetbrains.exodus.entitystore.PersistentEntityStoreImpl +import jetbrains.exodus.entitystore.PersistentEntityStores import kotlinx.coroutines.launch import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.client.connectToMagix @@ -14,7 +17,6 @@ import ru.mipt.npm.controls.controllers.install import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.rsocket.rSocketWithTcp -import ru.mipt.npm.magix.rsocket.rSocketWithWebSockets import ru.mipt.npm.magix.server.startMagixServer import space.kscience.dataforge.context.* import tornadofx.* @@ -24,6 +26,7 @@ class VirtualCarController : Controller(), ContextAware { var virtualCar: VirtualCar? = null var magixVirtualCar: MagixVirtualCar? = null var magixServer: ApplicationEngine? = null + var entityStore: PersistentEntityStore? = null override val context = Context("demoDevice") { plugin(DeviceManager) @@ -37,9 +40,14 @@ class VirtualCarController : Controller(), ContextAware { //starting magix event loop magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) + entityStore = PersistentEntityStores.newInstance("/home/marvel1337/2021/SCADA/.messages") //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) - deviceManager.connectToMagix(deviceEndpoint) + if (entityStore != null) { + deviceManager.connectToMagix(deviceEndpoint, entityStore = entityStore as PersistentEntityStoreImpl) + } else { + deviceManager.connectToMagix(deviceEndpoint) + } } } @@ -51,6 +59,8 @@ class VirtualCarController : Controller(), ContextAware { logger.info { "Magix virtual car server stopped" } virtualCar?.close() logger.info { "Virtual car server stopped" } + entityStore?.close() + logger.info { "Entity store closed" } context.close() } } From 1d8cc33c9153529a4f4cc7830c8a40ca029eff59 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Mon, 15 Nov 2021 17:18:06 +0300 Subject: [PATCH 20/74] Refactor connection to magix --- .../ru/mipt/npm/controls/client/dfMagix.kt | 15 +++--- .../mipt/npm/controls/client/xodusDfMagix.kt | 47 ++----------------- 2 files changed, 14 insertions(+), 48 deletions(-) diff --git a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt index 2e92205..8faed41 100644 --- a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt @@ -29,6 +29,7 @@ internal fun generateId(request: MagixMessage<*>): String = if (request.id != nu public fun DeviceManager.connectToMagix( endpoint: MagixEndpoint, endpointID: String = DATAFORGE_MAGIX_FORMAT, + preSendAction: (MagixMessage<*>) -> Unit = {} ): Job = context.launch { endpoint.subscribe().onEach { request -> val responsePayload = respondHubMessage(request.payload) @@ -48,13 +49,15 @@ public fun DeviceManager.connectToMagix( }.launchIn(this) hubMessageFlow(this).onEach { payload -> + val magixMessage = MagixMessage( + format = DATAFORGE_MAGIX_FORMAT, + id = "df[${payload.hashCode()}]", + origin = endpointID, + payload = payload + ) + preSendAction(magixMessage) endpoint.broadcast( - MagixMessage( - format = DATAFORGE_MAGIX_FORMAT, - id = "df[${payload.hashCode()}]", - origin = endpointID, - payload = payload - ) + magixMessage ) }.catch { error -> logger.error(error) { "Error while sending a message" } diff --git a/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt b/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt index 680b042..4a8cdc4 100644 --- a/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt +++ b/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt @@ -2,20 +2,11 @@ package ru.mipt.npm.controls.client import jetbrains.exodus.entitystore.PersistentEntityStore import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.catch -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.controllers.DeviceManager -import ru.mipt.npm.controls.controllers.hubMessageFlow -import ru.mipt.npm.controls.controllers.respondHubMessage import ru.mipt.npm.controls.xodus.toEntity import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixMessage -import space.kscience.dataforge.context.error -import space.kscience.dataforge.context.logger /** * Communicate with server in [Magix format](https://github.com/waltz-controls/rfc/tree/master/1) and dump messages at xodus entity store @@ -24,38 +15,10 @@ public fun DeviceManager.connectToMagix( endpoint: MagixEndpoint, endpointID: String = DATAFORGE_MAGIX_FORMAT, entityStore: PersistentEntityStore -): Job = context.launch { - endpoint.subscribe().onEach { request -> - val responsePayload = respondHubMessage(request.payload) - if (responsePayload != null) { - val response = MagixMessage( - format = DATAFORGE_MAGIX_FORMAT, - id = generateId(request), - parentId = request.id, - origin = endpointID, - payload = responsePayload - ) - - endpoint.broadcast(response) +): Job = connectToMagix(endpoint, endpointID) { message -> + if (message.payload is PropertyChangedMessage) { + entityStore.executeInTransaction { + message.toEntity(it) } - }.catch { error -> - logger.error(error) { "Error while responding to message" } - }.launchIn(this) - - hubMessageFlow(this).onEach { payload -> - val magixMessage = MagixMessage( - format = DATAFORGE_MAGIX_FORMAT, - id = "df[${payload.hashCode()}]", - origin = endpointID, - payload = payload - ) - endpoint.broadcast(magixMessage) - if (payload is PropertyChangedMessage) { - entityStore.executeInTransaction { - magixMessage.toEntity(it) - } - } - }.catch { error -> - logger.error(error) { "Error while sending a message" } - }.launchIn(this) + } } From 53f9af3ba49700a0d1c9fd95d8449ceb63b7b590 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Fri, 19 Nov 2021 22:15:50 +0300 Subject: [PATCH 21/74] Add converters from Entity and ConvertersTest --- .../ru/mipt/npm/controls/xodus/converters.kt | 38 ++++++++++++ .../mipt/npm/controls/xodus/ConvertersTest.kt | 59 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/ConvertersTest.kt diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt index bb9e070..83b3035 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt @@ -2,8 +2,13 @@ package ru.mipt.npm.controls.xodus import jetbrains.exodus.entitystore.Entity import jetbrains.exodus.entitystore.StoreTransaction +import kotlinx.datetime.Instant +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.magix.api.MagixMessage +import space.kscience.dataforge.meta.MetaSerializer +import space.kscience.dataforge.names.Name public fun PropertyChangedMessage.toEntity(transaction: StoreTransaction): Entity { val entity = transaction.newEntity("PropertyChangedMessage") @@ -16,6 +21,21 @@ public fun PropertyChangedMessage.toEntity(transaction: StoreTransaction): Entit return entity } +public fun Entity.toPropertyChangedMessage(): PropertyChangedMessage? { + if (getProperty("property") == null || getProperty("value") == null || getProperty("sourceDevice") == null) { + return null + } + + return PropertyChangedMessage( + getProperty("property") as String, + Json.decodeFromString(MetaSerializer, getProperty("value") as String), + Name.parse(getProperty("sourceDevice") as String), + getProperty("targetDevice")?.let { Name.parse(it as String) }, + getProperty("comment")?.let { it as String }, + getProperty("time")?.let { Instant.fromEpochMilliseconds(it as Long) } + ) +} + public fun MagixMessage.toEntity(transaction: StoreTransaction): Entity { val entity = transaction.newEntity("MagixMessage") entity.setProperty("format", format) @@ -30,3 +50,21 @@ public fun MagixMessage.toEntity(transaction: StoreTransaction): Entity { user?.let { entity.setProperty("user", it.toString()) } return entity } + +public fun Entity.toMagixMessage(): MagixMessage? { + if (getProperty("format") == null || getProperty("origin") == null) { + return null + } + + return getLink("payload")?.toPropertyChangedMessage()?.let { propertyChangedMessage -> + MagixMessage( + getProperty("format") as String, + getProperty("origin") as String, + propertyChangedMessage, + getProperty("target")?.let { it as String }, + getProperty("id")?.let { it as String }, + getProperty("parentId")?.let { it as String }, + getProperty("user")?.let { Json.decodeFromString(JsonElement.serializer(), it as String) } + ) + } +} diff --git a/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/ConvertersTest.kt b/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/ConvertersTest.kt new file mode 100644 index 0000000..85e8ba0 --- /dev/null +++ b/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/ConvertersTest.kt @@ -0,0 +1,59 @@ +package ru.mipt.npm.controls.xodus + +import jetbrains.exodus.entitystore.PersistentEntityStores +import kotlinx.datetime.Instant +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.magix.api.MagixMessage +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.names.Name +import java.io.File +import kotlin.test.assertEquals + +internal class ConvertersTest { + companion object { + private val entityStore = PersistentEntityStores.newInstance(".test") + private val expectedMessage = MagixMessage( + "dataforge", + "dataforge", + PropertyChangedMessage( + "acceleration", + Meta { + "x" put 3.0 + "y" put 9.0 + }, + Name.parse("virtual-car"), + Name.parse("magix-virtual-car"), + time = Instant.fromEpochMilliseconds(1337) + ), + "magix-virtual-car", + user = JsonObject(content = mapOf(Pair("name", JsonPrimitive("SCADA")))) + ) + + @BeforeAll + @JvmStatic + fun createEntities() { + entityStore.executeInTransaction { + expectedMessage.toEntity(it) + } + } + + @AfterAll + @JvmStatic + fun deleteDatabase() { + entityStore.close() + File(".test").deleteRecursively() + } + } + + @Test + fun testMagixMessageAndPropertyChangedMessageConverters() { + assertEquals(expectedMessage, entityStore.computeInReadonlyTransaction { + it.getAll("MagixMessage").first?.toMagixMessage() + }!!) + } +} From 099649f090f26e3de8bd50cc3bbbe0fc48115701 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Fri, 19 Nov 2021 23:03:54 +0300 Subject: [PATCH 22/74] Add fromTo util function for storeTransacton --- .../mipt/npm/controls/xodus/util/queries.kt | 15 +++++ .../mipt/npm/controls/xodus/ConvertersTest.kt | 5 +- .../npm/controls/xodus/util/QueriesTest.kt | 62 +++++++++++++++++++ 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt create mode 100644 controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt new file mode 100644 index 0000000..7771f35 --- /dev/null +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt @@ -0,0 +1,15 @@ +package ru.mipt.npm.controls.xodus.util + +import jetbrains.exodus.entitystore.StoreTransaction +import kotlinx.datetime.Instant +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.xodus.toPropertyChangedMessage + +public fun StoreTransaction.fromTo(from: Instant, to: Instant): List { + return find( + "PropertyChangedMessage", + "time", + from.toEpochMilliseconds(), + to.toEpochMilliseconds() + ).map { it -> it.toPropertyChangedMessage() }.filterNotNull() +} diff --git a/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/ConvertersTest.kt b/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/ConvertersTest.kt index 85e8ba0..da7f4d6 100644 --- a/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/ConvertersTest.kt +++ b/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/ConvertersTest.kt @@ -16,7 +16,8 @@ import kotlin.test.assertEquals internal class ConvertersTest { companion object { - private val entityStore = PersistentEntityStores.newInstance(".test") + private val storeName = ".converters_test" + private val entityStore = PersistentEntityStores.newInstance(storeName) private val expectedMessage = MagixMessage( "dataforge", "dataforge", @@ -46,7 +47,7 @@ internal class ConvertersTest { @JvmStatic fun deleteDatabase() { entityStore.close() - File(".test").deleteRecursively() + File(storeName).deleteRecursively() } } diff --git a/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt b/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt new file mode 100644 index 0000000..7da8bba --- /dev/null +++ b/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt @@ -0,0 +1,62 @@ +package ru.mipt.npm.controls.xodus.util + +import jetbrains.exodus.entitystore.PersistentEntityStores +import kotlinx.datetime.Instant +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.xodus.toEntity +import space.kscience.dataforge.meta.Meta +import java.io.File + +internal class QueriesTest { + companion object { + private val storeName = ".queries_test" + private val entityStore = PersistentEntityStores.newInstance(storeName) + + private val propertyChangedMessages = listOf( + PropertyChangedMessage( + "", + Meta.EMPTY, + time = Instant.fromEpochMilliseconds(1000) + ), + PropertyChangedMessage( + "", + Meta.EMPTY, + time = Instant.fromEpochMilliseconds(1500) + ), + PropertyChangedMessage( + "", + Meta.EMPTY, + time = Instant.fromEpochMilliseconds(2000) + ) + ) + + @BeforeAll + @JvmStatic + fun createEntities() { + entityStore.executeInTransaction { transaction -> + propertyChangedMessages.forEach { + it.toEntity(transaction) + } + } + } + + @AfterAll + @JvmStatic + fun deleteDatabase() { + entityStore.close() + File(storeName).deleteRecursively() + } + } + + @Test + fun testFromTo() { + assertEquals(propertyChangedMessages.subList(0, 2).toSet(), entityStore.computeInReadonlyTransaction { + it.fromTo(Instant.fromEpochMilliseconds(1000), Instant.fromEpochMilliseconds(1500)) + }.toSet()) + } + +} \ No newline at end of file From 85f6b81334f33f854f210da26f469ff2ed461252 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 20 Nov 2021 13:13:32 +0300 Subject: [PATCH 23/74] Architecture rework --- .../controls/controllers/deviceMessages.kt | 2 ++ controls-magix-client/build.gradle.kts | 6 ---- .../mipt/npm/controls/client/xodusDfMagix.kt | 36 ++++++++----------- controls-xodus/build.gradle.kts | 7 ++-- .../ru/mipt/npm/controls/xodus/converters.kt | 16 ++++++++- .../ru/mipt/npm/controls/xodus/device.kt | 21 +++++++++++ demo/car/build.gradle.kts | 1 + .../npm/controls/demo/car/MagixVirtualCar.kt | 3 +- .../controls/demo/car/VirtualCarController.kt | 15 ++++---- 9 files changed, 66 insertions(+), 41 deletions(-) create mode 100644 controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/device.kt diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/deviceMessages.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/deviceMessages.kt index 5158961..71d8cf9 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/deviceMessages.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/deviceMessages.kt @@ -95,6 +95,8 @@ public suspend fun DeviceHub.respondHubMessage(request: DeviceMessage): DeviceMe * Collect all messages from given [DeviceHub], applying proper relative names */ public fun DeviceHub.hubMessageFlow(scope: CoroutineScope): Flow { + + //TODO could we avoid using downstream scope? val outbox = MutableSharedFlow() if (this is Device) { messageFlow.onEach { diff --git a/controls-magix-client/build.gradle.kts b/controls-magix-client/build.gradle.kts index 6acc75f..7941c2e 100644 --- a/controls-magix-client/build.gradle.kts +++ b/controls-magix-client/build.gradle.kts @@ -17,11 +17,5 @@ kotlin { implementation(project(":controls-core")) } } - jvmMain { - dependencies { - implementation("org.jetbrains.xodus:xodus-openAPI:1.3.232") - implementation(project(":controls-xodus")) - } - } } } diff --git a/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt b/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt index 4a8cdc4..f06cd55 100644 --- a/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt +++ b/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt @@ -1,24 +1,16 @@ package ru.mipt.npm.controls.client -import jetbrains.exodus.entitystore.PersistentEntityStore -import kotlinx.coroutines.Job -import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.controllers.DeviceManager -import ru.mipt.npm.controls.xodus.toEntity -import ru.mipt.npm.magix.api.MagixEndpoint - -/** - * Communicate with server in [Magix format](https://github.com/waltz-controls/rfc/tree/master/1) and dump messages at xodus entity store - */ -public fun DeviceManager.connectToMagix( - endpoint: MagixEndpoint, - endpointID: String = DATAFORGE_MAGIX_FORMAT, - entityStore: PersistentEntityStore -): Job = connectToMagix(endpoint, endpointID) { message -> - if (message.payload is PropertyChangedMessage) { - entityStore.executeInTransaction { - message.toEntity(it) - } - } -} +///** +// * Communicate with server in [Magix format](https://github.com/waltz-controls/rfc/tree/master/1) and dump messages at xodus entity store +// */ +//public fun DeviceManager.connectToMagix( +// endpoint: MagixEndpoint, +// endpointID: String = DATAFORGE_MAGIX_FORMAT, +// entityStore: PersistentEntityStore +//): Job = connectToMagix(endpoint, endpointID) { message -> +// if (message.payload is PropertyChangedMessage) { +// entityStore.executeInTransaction { +// message.toEntity(it) +// } +// } +//} diff --git a/controls-xodus/build.gradle.kts b/controls-xodus/build.gradle.kts index e6dc62b..bf1e98d 100644 --- a/controls-xodus/build.gradle.kts +++ b/controls-xodus/build.gradle.kts @@ -3,11 +3,12 @@ plugins { `maven-publish` } +val xodusVersion = "1.3.232" dependencies { implementation(projects.controlsCore) implementation(projects.magix.magixApi) - implementation("org.jetbrains.xodus:xodus-entity-store:1.3.232") - implementation("org.jetbrains.xodus:xodus-environment:1.3.232") - implementation("org.jetbrains.xodus:xodus-vfs:1.3.232") + implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") + implementation("org.jetbrains.xodus:xodus-environment:$xodusVersion") + implementation("org.jetbrains.xodus:xodus-vfs:$xodusVersion") } \ No newline at end of file diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt index 83b3035..76bdb63 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt @@ -8,12 +8,26 @@ import kotlinx.serialization.json.JsonElement import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.magix.api.MagixMessage import space.kscience.dataforge.meta.MetaSerializer +import space.kscience.dataforge.meta.isLeaf import space.kscience.dataforge.names.Name +import space.kscience.dataforge.values.Value +import space.kscience.dataforge.values.ValueType public fun PropertyChangedMessage.toEntity(transaction: StoreTransaction): Entity { val entity = transaction.newEntity("PropertyChangedMessage") entity.setProperty("property", property) - entity.setProperty("value", value.toString()) + if (value.isLeaf) { + val v: Value = value.value ?: TODO() + when(v.type){ + ValueType.NULL -> TODO() + ValueType.LIST -> TODO() + ValueType.NUMBER -> TODO() + ValueType.STRING -> TODO() + ValueType.BOOLEAN -> TODO() + } + } else { + entity.setProperty("value", value.toString()) + } entity.setProperty("sourceDevice", sourceDevice.toString()) targetDevice?.let { entity.setProperty("targetDevice", it.toString()) } comment?.let { entity.setProperty("comment", it) } diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/device.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/device.kt new file mode 100644 index 0000000..c4c2d40 --- /dev/null +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/device.kt @@ -0,0 +1,21 @@ +package ru.mipt.npm.controls.xodus + +import jetbrains.exodus.entitystore.PersistentEntityStore +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.controllers.DeviceManager +import ru.mipt.npm.controls.controllers.hubMessageFlow + + +public fun DeviceManager.connectXodus( + entityStore: PersistentEntityStore, + //filter: (DeviceMessage) -> Boolean = {it is PropertyChangedMessage} +): Job = hubMessageFlow(context).onEach { message -> + if (message is PropertyChangedMessage) { + entityStore.executeInTransaction { + message.toEntity(it) + } + } +}.launchIn(context) \ No newline at end of file diff --git a/demo/car/build.gradle.kts b/demo/car/build.gradle.kts index c5b3da5..3427645 100644 --- a/demo/car/build.gradle.kts +++ b/demo/car/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { implementation(projects.magix.magixServer) implementation(projects.magix.magixRsocket) implementation(projects.controlsMagixClient) + implementation(projects.controlsXodus) implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1") diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt index 3d0c97c..bee2464 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt @@ -14,8 +14,7 @@ import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name import kotlin.time.ExperimentalTime -class MagixVirtualCar(context: Context, meta: Meta) - : VirtualCar(context, meta) { +class MagixVirtualCar(context: Context, meta: Meta) : VirtualCar(context, meta) { private suspend fun MagixEndpoint.startMagixVirtualCarUpdate() { launch { diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index a8f5e54..90489c3 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -1,20 +1,21 @@ package ru.mipt.npm.controls.demo.car -import io.ktor.server.engine.* +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 jetbrains.exodus.entitystore.PersistentEntityStore -import jetbrains.exodus.entitystore.PersistentEntityStoreImpl import jetbrains.exodus.entitystore.PersistentEntityStores +import kotlinx.coroutines.Job import kotlinx.coroutines.launch 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.car.IVirtualCar.Companion.acceleration +import ru.mipt.npm.controls.xodus.connectXodus import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.rsocket.rSocketWithTcp import ru.mipt.npm.magix.server.startMagixServer @@ -27,6 +28,7 @@ class VirtualCarController : Controller(), ContextAware { var magixVirtualCar: MagixVirtualCar? = null var magixServer: ApplicationEngine? = null var entityStore: PersistentEntityStore? = null + var storageJob: Job? =null override val context = Context("demoDevice") { plugin(DeviceManager) @@ -37,17 +39,16 @@ class VirtualCarController : Controller(), ContextAware { fun init() { context.launch { virtualCar = deviceManager.install("virtual-car", VirtualCar) + //starting magix event loop magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) entityStore = PersistentEntityStores.newInstance("/home/marvel1337/2021/SCADA/.messages") + //connect to entity store + storageJob = deviceManager.connectXodus(entityStore as PersistentEntityStore) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) - if (entityStore != null) { - deviceManager.connectToMagix(deviceEndpoint, entityStore = entityStore as PersistentEntityStoreImpl) - } else { - deviceManager.connectToMagix(deviceEndpoint) - } + deviceManager.connectToMagix(deviceEndpoint) } } From 0b14c7ed7f0d802fa2fc12712343faa6a86df543 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Fri, 26 Nov 2021 16:25:09 +0300 Subject: [PATCH 24/74] Make it work --- .../ru/mipt/npm/controls/client/xodusDfMagix.kt | 16 ---------------- controls-xodus/build.gradle.kts | 1 + .../ru/mipt/npm/controls/xodus/converters.kt | 13 +------------ .../kotlin/ru/mipt/npm/controls/xodus/device.kt | 12 ++++++------ 4 files changed, 8 insertions(+), 34 deletions(-) delete mode 100644 controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt diff --git a/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt b/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt deleted file mode 100644 index f06cd55..0000000 --- a/controls-magix-client/src/jvmMain/kotlin/ru/mipt/npm/controls/client/xodusDfMagix.kt +++ /dev/null @@ -1,16 +0,0 @@ -package ru.mipt.npm.controls.client - -///** -// * Communicate with server in [Magix format](https://github.com/waltz-controls/rfc/tree/master/1) and dump messages at xodus entity store -// */ -//public fun DeviceManager.connectToMagix( -// endpoint: MagixEndpoint, -// endpointID: String = DATAFORGE_MAGIX_FORMAT, -// entityStore: PersistentEntityStore -//): Job = connectToMagix(endpoint, endpointID) { message -> -// if (message.payload is PropertyChangedMessage) { -// entityStore.executeInTransaction { -// message.toEntity(it) -// } -// } -//} diff --git a/controls-xodus/build.gradle.kts b/controls-xodus/build.gradle.kts index bf1e98d..f6d7c73 100644 --- a/controls-xodus/build.gradle.kts +++ b/controls-xodus/build.gradle.kts @@ -8,6 +8,7 @@ val xodusVersion = "1.3.232" dependencies { implementation(projects.controlsCore) implementation(projects.magix.magixApi) + implementation(projects.controlsMagixClient) implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") implementation("org.jetbrains.xodus:xodus-environment:$xodusVersion") implementation("org.jetbrains.xodus:xodus-vfs:$xodusVersion") diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt index 76bdb63..bc7deb6 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt @@ -16,18 +16,7 @@ import space.kscience.dataforge.values.ValueType public fun PropertyChangedMessage.toEntity(transaction: StoreTransaction): Entity { val entity = transaction.newEntity("PropertyChangedMessage") entity.setProperty("property", property) - if (value.isLeaf) { - val v: Value = value.value ?: TODO() - when(v.type){ - ValueType.NULL -> TODO() - ValueType.LIST -> TODO() - ValueType.NUMBER -> TODO() - ValueType.STRING -> TODO() - ValueType.BOOLEAN -> TODO() - } - } else { - entity.setProperty("value", value.toString()) - } + entity.setProperty("value", value.toString()) entity.setProperty("sourceDevice", sourceDevice.toString()) targetDevice?.let { entity.setProperty("targetDevice", it.toString()) } comment?.let { entity.setProperty("comment", it) } diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/device.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/device.kt index c4c2d40..ea85fed 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/device.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/device.kt @@ -2,8 +2,10 @@ package ru.mipt.npm.controls.xodus import jetbrains.exodus.entitystore.PersistentEntityStore import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.hubMessageFlow @@ -11,11 +13,9 @@ import ru.mipt.npm.controls.controllers.hubMessageFlow public fun DeviceManager.connectXodus( entityStore: PersistentEntityStore, - //filter: (DeviceMessage) -> Boolean = {it is PropertyChangedMessage} -): Job = hubMessageFlow(context).onEach { message -> - if (message is PropertyChangedMessage) { - entityStore.executeInTransaction { - message.toEntity(it) - } + filterCondition: suspend (DeviceMessage) -> Boolean = { it is PropertyChangedMessage } +): Job = hubMessageFlow(context).filter(filterCondition).onEach { message -> + entityStore.executeInTransaction { + (message as PropertyChangedMessage).toEntity(it) } }.launchIn(context) \ No newline at end of file From 3639783b4ed59a0becf32c8e2354ca04844ecaa6 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Fri, 26 Nov 2021 17:26:41 +0300 Subject: [PATCH 25/74] Add connection to xodus from magix server --- controls-xodus/build.gradle.kts | 1 + .../ru/mipt/npm/controls/xodus/connections.kt | 48 +++++++++++++++++++ .../ru/mipt/npm/controls/xodus/device.kt | 21 -------- .../controls/demo/car/VirtualCarController.kt | 23 +++++---- 4 files changed, 63 insertions(+), 30 deletions(-) create mode 100644 controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt delete mode 100644 controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/device.kt diff --git a/controls-xodus/build.gradle.kts b/controls-xodus/build.gradle.kts index f6d7c73..cf06c4c 100644 --- a/controls-xodus/build.gradle.kts +++ b/controls-xodus/build.gradle.kts @@ -9,6 +9,7 @@ dependencies { implementation(projects.controlsCore) implementation(projects.magix.magixApi) implementation(projects.controlsMagixClient) + implementation(projects.magix.magixServer) implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") implementation("org.jetbrains.xodus:xodus-environment:$xodusVersion") implementation("org.jetbrains.xodus:xodus-vfs:$xodusVersion") diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt new file mode 100644 index 0000000..4a35bd1 --- /dev/null +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt @@ -0,0 +1,48 @@ +package ru.mipt.npm.controls.xodus + +import io.ktor.application.* +import io.ktor.server.engine.* +import jetbrains.exodus.entitystore.PersistentEntityStore +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import ru.mipt.npm.controls.api.DeviceMessage +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.controllers.DeviceManager +import ru.mipt.npm.controls.controllers.hubMessageFlow +import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.server.GenericMagixMessage +import ru.mipt.npm.magix.server.startMagixServer + + +public fun DeviceManager.connectXodus( + entityStore: PersistentEntityStore, + filterCondition: suspend (DeviceMessage) -> Boolean = { it is PropertyChangedMessage } +): Job = hubMessageFlow(context).filter(filterCondition).onEach { message -> + entityStore.executeInTransaction { + (message as PropertyChangedMessage).toEntity(it) + } +}.launchIn(context) + +public fun CoroutineScope.startMagixServer( + entityStore: PersistentEntityStore, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, + port: Int = MagixEndpoint.DEFAULT_MAGIX_HTTP_PORT, + buffer: Int = 100, + enableRawRSocket: Boolean = true, + enableZmq: Boolean = true, + applicationConfiguration: Application.(MutableSharedFlow) -> Unit = {}, +): ApplicationEngine = startMagixServer( + port, buffer, enableRawRSocket, enableZmq +) { flow -> + applicationConfiguration(flow) + flow.filter(flowFilter).onEach { message -> + entityStore.executeInTransaction { txn -> + val entity = txn.newEntity("MagixMessage") + entity.setProperty("value", message.toString()) + } + } +} diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/device.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/device.kt deleted file mode 100644 index ea85fed..0000000 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/device.kt +++ /dev/null @@ -1,21 +0,0 @@ -package ru.mipt.npm.controls.xodus - -import jetbrains.exodus.entitystore.PersistentEntityStore -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.controllers.DeviceManager -import ru.mipt.npm.controls.controllers.hubMessageFlow - - -public fun DeviceManager.connectXodus( - entityStore: PersistentEntityStore, - filterCondition: suspend (DeviceMessage) -> Boolean = { it is PropertyChangedMessage } -): Job = hubMessageFlow(context).filter(filterCondition).onEach { message -> - entityStore.executeInTransaction { - (message as PropertyChangedMessage).toEntity(it) - } -}.launchIn(context) \ No newline at end of file diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 90489c3..ed701ab 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -18,7 +18,7 @@ import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration import ru.mipt.npm.controls.xodus.connectXodus import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.rsocket.rSocketWithTcp -import ru.mipt.npm.magix.server.startMagixServer +import ru.mipt.npm.controls.xodus.startMagixServer import space.kscience.dataforge.context.* import tornadofx.* @@ -27,7 +27,8 @@ class VirtualCarController : Controller(), ContextAware { var virtualCar: VirtualCar? = null var magixVirtualCar: MagixVirtualCar? = null var magixServer: ApplicationEngine? = null - var entityStore: PersistentEntityStore? = null + var deviceEntityStore: PersistentEntityStore? = null + var magixEntityStore: PersistentEntityStore? = null var storageJob: Job? =null override val context = Context("demoDevice") { @@ -40,12 +41,14 @@ class VirtualCarController : Controller(), ContextAware { context.launch { virtualCar = deviceManager.install("virtual-car", VirtualCar) - //starting magix event loop - magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) + //starting magix event loop and connect it to entity store + magixEntityStore = PersistentEntityStores.newInstance("/home/marvel1337/2021/SCADA/.server_messages") + magixServer = startMagixServer( + entityStore = magixEntityStore as PersistentEntityStore, enableRawRSocket = true, enableZmq = true) magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) - entityStore = PersistentEntityStores.newInstance("/home/marvel1337/2021/SCADA/.messages") - //connect to entity store - storageJob = deviceManager.connectXodus(entityStore as PersistentEntityStore) + deviceEntityStore = PersistentEntityStores.newInstance("/home/marvel1337/2021/SCADA/.messages") + //connect to device entity store + storageJob = deviceManager.connectXodus(deviceEntityStore as PersistentEntityStore) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) deviceManager.connectToMagix(deviceEndpoint) @@ -60,8 +63,10 @@ class VirtualCarController : Controller(), ContextAware { logger.info { "Magix virtual car server stopped" } virtualCar?.close() logger.info { "Virtual car server stopped" } - entityStore?.close() - logger.info { "Entity store closed" } + deviceEntityStore?.close() + logger.info { "Device entity store closed" } + magixEntityStore?.close() + logger.info { "Magix entity store closed" } context.close() } } From 312cd06706f4ce1b36fb16997ffa6fafbc46103a Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Sat, 27 Nov 2021 00:52:14 +0300 Subject: [PATCH 26/74] Add mongo module --- controls-mongo/build.gradle.kts | 13 ++++++ .../ru/mipt/npm/controls/mongo/connections.kt | 43 +++++++++++++++++++ demo/car/build.gradle.kts | 2 + .../controls/demo/car/VirtualCarController.kt | 15 ++++++- settings.gradle.kts | 5 ++- 5 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 controls-mongo/build.gradle.kts create mode 100644 controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt diff --git a/controls-mongo/build.gradle.kts b/controls-mongo/build.gradle.kts new file mode 100644 index 0000000..e6ce49d --- /dev/null +++ b/controls-mongo/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("ru.mipt.npm.gradle.jvm") + `maven-publish` +} + +val kmongoVersion = "4.4.0" + +dependencies { + implementation(projects.controlsCore) + implementation(projects.magix.magixApi) + implementation(projects.controlsMagixClient) + implementation("org.litote.kmongo:kmongo-coroutine-serialization:$kmongoVersion") +} diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt new file mode 100644 index 0000000..950dd31 --- /dev/null +++ b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt @@ -0,0 +1,43 @@ +package ru.mipt.npm.controls.mongo + +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import org.litote.kmongo.coroutine.CoroutineClient +import org.litote.kmongo.coroutine.coroutine +import org.litote.kmongo.coroutine.insertOne +import org.litote.kmongo.reactivestreams.KMongo +import ru.mipt.npm.controls.api.DeviceMessage +import ru.mipt.npm.controls.controllers.DeviceManager +import ru.mipt.npm.controls.controllers.hubMessageFlow +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.string + +public object MongoClientFactory : Factory { + public const val connectionString: String = "mongodb://mongoadmin:secret@localhost:27888" + + override fun invoke(meta: Meta, context: Context): CoroutineClient { + return meta["connectionString"]?.string?.let { + KMongo.createClient(it).coroutine + } ?: KMongo.createClient(connectionString).coroutine + } +} + +public fun DeviceManager.connectMongo( + client: CoroutineClient, + filterCondition: suspend (DeviceMessage) -> Boolean = { true } +): Job = hubMessageFlow(context).filter(filterCondition).onEach { message -> + context.launch { + client + .getDatabase("deviceServer") + .getCollection() + .insertOne(Json.encodeToString(message)) + } +}.launchIn(context) diff --git a/demo/car/build.gradle.kts b/demo/car/build.gradle.kts index 3427645..ad3dda1 100644 --- a/demo/car/build.gradle.kts +++ b/demo/car/build.gradle.kts @@ -20,6 +20,7 @@ dependencies { implementation(projects.magix.magixRsocket) implementation(projects.controlsMagixClient) implementation(projects.controlsXodus) + implementation(projects.controlsMongo) implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1") @@ -29,6 +30,7 @@ dependencies { implementation("org.jetbrains.xodus:xodus-entity-store:1.3.232") implementation("org.jetbrains.xodus:xodus-environment:1.3.232") implementation("org.jetbrains.xodus:xodus-vfs:1.3.232") + implementation("org.litote.kmongo:kmongo-coroutine-serialization:4.4.0") } tasks.withType().configureEach { diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index ed701ab..f9950fb 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -10,16 +10,20 @@ import jetbrains.exodus.entitystore.PersistentEntityStore import jetbrains.exodus.entitystore.PersistentEntityStores import kotlinx.coroutines.Job import kotlinx.coroutines.launch +import org.litote.kmongo.coroutine.CoroutineClient 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.car.IVirtualCar.Companion.acceleration +import ru.mipt.npm.controls.mongo.MongoClientFactory +import ru.mipt.npm.controls.mongo.connectMongo import ru.mipt.npm.controls.xodus.connectXodus import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.rsocket.rSocketWithTcp import ru.mipt.npm.controls.xodus.startMagixServer import space.kscience.dataforge.context.* +import space.kscience.dataforge.meta.Meta import tornadofx.* class VirtualCarController : Controller(), ContextAware { @@ -29,7 +33,9 @@ class VirtualCarController : Controller(), ContextAware { var magixServer: ApplicationEngine? = null var deviceEntityStore: PersistentEntityStore? = null var magixEntityStore: PersistentEntityStore? = null - var storageJob: Job? =null + var mongoClient: CoroutineClient? = null + var xodusStorageJob: Job? = null + var mongoStorageJob: Job? = null override val context = Context("demoDevice") { plugin(DeviceManager) @@ -48,7 +54,10 @@ class VirtualCarController : Controller(), ContextAware { magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) deviceEntityStore = PersistentEntityStores.newInstance("/home/marvel1337/2021/SCADA/.messages") //connect to device entity store - storageJob = deviceManager.connectXodus(deviceEntityStore as PersistentEntityStore) + xodusStorageJob = deviceManager.connectXodus(deviceEntityStore as PersistentEntityStore) + //Create mongo client and connect to MongoDB + mongoClient = MongoClientFactory.invoke(meta = Meta.EMPTY, context) + mongoStorageJob = deviceManager.connectMongo(mongoClient as CoroutineClient) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) deviceManager.connectToMagix(deviceEndpoint) @@ -67,6 +76,8 @@ class VirtualCarController : Controller(), ContextAware { logger.info { "Device entity store closed" } magixEntityStore?.close() logger.info { "Magix entity store closed" } + mongoClient?.close() + logger.info { "MongoClient closed" } context.close() } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 2b76cf6..9d25625 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -51,5 +51,6 @@ include( ":magix:magix-demo", ":controls-magix-client", ":motors", - ":controls-xodus" -) \ No newline at end of file + ":controls-xodus", + ":controls-mongo" +) From 5da155988297e6fb4d114f376a9c5f628aa790f7 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 27 Nov 2021 13:37:39 +0300 Subject: [PATCH 27/74] Architecture rework --- .../ru/mipt/npm/controls/mongo/connections.kt | 14 +++--- .../ru/mipt/npm/controls/xodus/connections.kt | 43 +++++++++++-------- .../mipt/npm/controls/xodus/util/queries.kt | 19 ++++---- .../npm/controls/xodus/util/QueriesTest.kt | 2 +- .../controls/demo/car/VirtualCarController.kt | 13 +++--- motors/build.gradle.kts | 1 + .../pimotionmaster/PiMotionMasterApp.kt | 4 +- 7 files changed, 56 insertions(+), 40 deletions(-) diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt index 950dd31..d5157c1 100644 --- a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt +++ b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt @@ -23,11 +23,9 @@ import space.kscience.dataforge.meta.string public object MongoClientFactory : Factory { public const val connectionString: String = "mongodb://mongoadmin:secret@localhost:27888" - override fun invoke(meta: Meta, context: Context): CoroutineClient { - return meta["connectionString"]?.string?.let { - KMongo.createClient(it).coroutine - } ?: KMongo.createClient(connectionString).coroutine - } + override fun invoke(meta: Meta, context: Context): CoroutineClient = meta["connectionString"]?.string?.let { + KMongo.createClient(it).coroutine + } ?: KMongo.createClient(connectionString).coroutine } public fun DeviceManager.connectMongo( @@ -40,4 +38,8 @@ public fun DeviceManager.connectMongo( .getCollection() .insertOne(Json.encodeToString(message)) } -}.launchIn(context) +}.launchIn(context).apply { + invokeOnCompletion { + client.close() + } +} diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt index 4a35bd1..4aa1be6 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt @@ -1,11 +1,8 @@ package ru.mipt.npm.controls.xodus -import io.ktor.application.* -import io.ktor.server.engine.* import jetbrains.exodus.entitystore.PersistentEntityStore -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -13,9 +10,7 @@ import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.hubMessageFlow -import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.server.GenericMagixMessage -import ru.mipt.npm.magix.server.startMagixServer public fun DeviceManager.connectXodus( @@ -27,22 +22,34 @@ public fun DeviceManager.connectXodus( } }.launchIn(context) -public fun CoroutineScope.startMagixServer( +//public fun CoroutineScope.startMagixServer( +// entityStore: PersistentEntityStore, +// flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, +// port: Int = MagixEndpoint.DEFAULT_MAGIX_HTTP_PORT, +// buffer: Int = 100, +// enableRawRSocket: Boolean = true, +// enableZmq: Boolean = true, +// applicationConfiguration: Application.(MutableSharedFlow) -> Unit = {}, +//): ApplicationEngine = startMagixServer( +// port, buffer, enableRawRSocket, enableZmq +//) { flow -> +// applicationConfiguration(flow) +// flow.filter(flowFilter).onEach { message -> +// entityStore.executeInTransaction { txn -> +// val entity = txn.newEntity("MagixMessage") +// entity.setProperty("value", message.toString()) +// } +// } +//} + +public fun SharedFlow.storeInXodus( entityStore: PersistentEntityStore, flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, - port: Int = MagixEndpoint.DEFAULT_MAGIX_HTTP_PORT, - buffer: Int = 100, - enableRawRSocket: Boolean = true, - enableZmq: Boolean = true, - applicationConfiguration: Application.(MutableSharedFlow) -> Unit = {}, -): ApplicationEngine = startMagixServer( - port, buffer, enableRawRSocket, enableZmq -) { flow -> - applicationConfiguration(flow) - flow.filter(flowFilter).onEach { message -> +){ + filter(flowFilter).onEach { message -> entityStore.executeInTransaction { txn -> val entity = txn.newEntity("MagixMessage") entity.setProperty("value", message.toString()) } } -} +} \ No newline at end of file diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt index 7771f35..f547e05 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt @@ -5,11 +5,14 @@ import kotlinx.datetime.Instant import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.xodus.toPropertyChangedMessage -public fun StoreTransaction.fromTo(from: Instant, to: Instant): List { - return find( - "PropertyChangedMessage", - "time", - from.toEpochMilliseconds(), - to.toEpochMilliseconds() - ).map { it -> it.toPropertyChangedMessage() }.filterNotNull() -} +//selectDeviceMessagesInRange +public fun StoreTransaction.fromTo( + range: ClosedRange, +// from: Instant, +// to: Instant, +): List = find( + "PropertyChangedMessage", + "time", + range.start.toEpochMilliseconds(), + range.endInclusive.toEpochMilliseconds() +).mapNotNull { it.toPropertyChangedMessage() } diff --git a/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt b/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt index 7da8bba..73d915b 100644 --- a/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt +++ b/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt @@ -55,7 +55,7 @@ internal class QueriesTest { @Test fun testFromTo() { assertEquals(propertyChangedMessages.subList(0, 2).toSet(), entityStore.computeInReadonlyTransaction { - it.fromTo(Instant.fromEpochMilliseconds(1000), Instant.fromEpochMilliseconds(1500)) + it.fromTo( Instant.fromEpochMilliseconds(1000)..Instant.fromEpochMilliseconds(1500)) }.toSet()) } diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index f9950fb..8a652bb 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -19,9 +19,10 @@ import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration import ru.mipt.npm.controls.mongo.MongoClientFactory import ru.mipt.npm.controls.mongo.connectMongo import ru.mipt.npm.controls.xodus.connectXodus +import ru.mipt.npm.controls.xodus.storeInXodus import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.rsocket.rSocketWithTcp -import ru.mipt.npm.controls.xodus.startMagixServer +import ru.mipt.npm.magix.server.startMagixServer import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta import tornadofx.* @@ -49,15 +50,17 @@ class VirtualCarController : Controller(), ContextAware { //starting magix event loop and connect it to entity store magixEntityStore = PersistentEntityStores.newInstance("/home/marvel1337/2021/SCADA/.server_messages") - magixServer = startMagixServer( - entityStore = magixEntityStore as PersistentEntityStore, enableRawRSocket = true, enableZmq = true) + magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> + flow.storeInXodus(magixEntityStore as PersistentEntityStore) + } magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) deviceEntityStore = PersistentEntityStores.newInstance("/home/marvel1337/2021/SCADA/.messages") //connect to device entity store xodusStorageJob = deviceManager.connectXodus(deviceEntityStore as PersistentEntityStore) //Create mongo client and connect to MongoDB - mongoClient = MongoClientFactory.invoke(meta = Meta.EMPTY, context) - mongoStorageJob = deviceManager.connectMongo(mongoClient as CoroutineClient) + val mongoClient = MongoClientFactory.invoke(meta = Meta.EMPTY, context) + mongoStorageJob = deviceManager.connectMongo(mongoClient) + this@VirtualCarController.mongoClient = mongoClient //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) deviceManager.connectToMagix(deviceEndpoint) diff --git a/motors/build.gradle.kts b/motors/build.gradle.kts index a2bd21d..503ab5d 100644 --- a/motors/build.gradle.kts +++ b/motors/build.gradle.kts @@ -19,6 +19,7 @@ kscience{ } val ktorVersion: String by rootProject.extra +val dataforgeVersion: String by extra dependencies { implementation(project(":controls-tcp")) 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 cf66c82..76fc7d2 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 @@ -16,7 +16,7 @@ import ru.mipt.npm.controls.controllers.installing import ru.mipt.npm.devices.pimotionmaster.PiMotionMasterDevice.Axis.Companion.maxPosition import ru.mipt.npm.devices.pimotionmaster.PiMotionMasterDevice.Axis.Companion.minPosition import ru.mipt.npm.devices.pimotionmaster.PiMotionMasterDevice.Axis.Companion.position -import space.kscience.dataforge.context.Global +import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.fetch import tornadofx.* @@ -24,7 +24,7 @@ class PiMotionMasterApp : App(PiMotionMasterView::class) class PiMotionMasterController : Controller() { //initialize context - val context = Global.buildContext("piMotionMaster"){ + val context = Context("piMotionMaster"){ plugin(DeviceManager) } From f77f6b7211580c83fd7e959066a735e79491dba4 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Fri, 3 Dec 2021 23:29:13 +0300 Subject: [PATCH 28/74] Add encoding and decoding to/from entity by json encoding/decoding --- settings.gradle.kts | 4 +- xodus-serialization/build.gradle.kts | 20 +++++++ .../npm/xodus/serialization/json/decoder.kt | 42 ++++++++++++++ .../npm/xodus/serialization/json/encoder.kt | 58 +++++++++++++++++++ .../mipt/npm/xodus/serialization/json/main.kt | 39 +++++++++++++ 5 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 xodus-serialization/build.gradle.kts create mode 100644 xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt create mode 100644 xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt create mode 100644 xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt diff --git a/settings.gradle.kts b/settings.gradle.kts index 9d25625..896d04d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -52,5 +52,7 @@ include( ":controls-magix-client", ":motors", ":controls-xodus", - ":controls-mongo" + ":controls-mongo", + ":xodus-serialization" ) +include("xodus-serialization") diff --git a/xodus-serialization/build.gradle.kts b/xodus-serialization/build.gradle.kts new file mode 100644 index 0000000..dad94f5 --- /dev/null +++ b/xodus-serialization/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + id("ru.mipt.npm.gradle.jvm") + `maven-publish` +} + +val xodusVersion = "1.3.232" + +kscience { + useSerialization { + json() + } +} + +dependencies { + implementation(projects.magix.magixApi) + implementation(projects.controlsCore) + implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") + implementation("org.jetbrains.xodus:xodus-environment:$xodusVersion") + implementation("org.jetbrains.xodus:xodus-vfs:$xodusVersion") +} diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt new file mode 100644 index 0000000..38e4a16 --- /dev/null +++ b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt @@ -0,0 +1,42 @@ +package ru.mipt.npm.xodus.serialization.json + +import jetbrains.exodus.entitystore.Entity +import jetbrains.exodus.entitystore.StoreTransaction +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.json.* +import kotlinx.serialization.serializer + +internal fun StoreTransaction.decodeFromEntity(entity: Entity): JsonElement = buildJsonObject { + entity.propertyNames.forEach { property -> + entity.getProperty(property).let { value -> + when (value) { + is Number -> put(property, value) + is Boolean -> put(property, value) + is String -> put(property, value) + else -> throw IllegalStateException("Unsupported type for primitive field") + } + } + } + + entity.linkNames.forEach { link -> + entity.getLinks(link).let { entities -> + when (entities.size()) { + 1L -> entities.first?.let { put(link, decodeFromEntity(it)) } + else -> { + putJsonArray(link) { + entities.forEach { + add(decodeFromEntity(it)) + } + } + } + } + } + } +} + +public fun StoreTransaction.decodeFromEntity(entity: Entity, deserializer: DeserializationStrategy): T { + val jsonElement = decodeFromEntity(entity) + return Json.decodeFromJsonElement(deserializer, jsonElement) +} + +public inline fun StoreTransaction.decodeFromEntity(entity: Entity): T = decodeFromEntity(entity, serializer()) diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt new file mode 100644 index 0000000..87173f9 --- /dev/null +++ b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt @@ -0,0 +1,58 @@ +package ru.mipt.npm.xodus.serialization.json + +import jetbrains.exodus.entitystore.Entity +import jetbrains.exodus.entitystore.StoreTransaction +import kotlinx.serialization.SerializationStrategy +import kotlinx.serialization.json.* +import kotlinx.serialization.serializer + +internal fun StoreTransaction.encodeToEntity(jsonElement: JsonElement, entity: Entity) { + when (jsonElement) { + is JsonPrimitive -> throw IllegalStateException("Can't serialize primitive value to entity") + is JsonArray -> throw IllegalStateException("Can't serialize array value to entity") + is JsonObject -> { + jsonElement.forEach { entry -> + entry.value.let { value -> + when(value) { + is JsonPrimitive -> { + if (value.isString) { + entity.setProperty(entry.key, value.content) + } else { + (value.longOrNull ?: value.doubleOrNull ?: value.booleanOrNull)?.let { + entity.setProperty( + entry.key, + it + ) + } + } + } + + // считаем, что все элементы массива - JsonObject, иначе не можем напрямую сериализовать (надо придывать костыли???) + is JsonArray -> { + value.forEach { element -> + val childEntity = newEntity("${entity.type}.${entry.key}") + encodeToEntity(element, childEntity) + entity.addLink(entry.key, childEntity) + } + } + + is JsonObject -> { + val childEntity = newEntity("${entity.type}.${entry.key}") + encodeToEntity(value, childEntity) + entity.setLink(entry.key, childEntity) + } + } + } + } + } + } +} + +public fun StoreTransaction.encodeToEntity(serializer: SerializationStrategy, value: T, entityType: String): Entity { + val entity: Entity = newEntity(entityType) + encodeToEntity(Json.encodeToJsonElement(serializer, value), entity) + return entity +} + +public inline fun StoreTransaction.encodeToEntity(value: T, entityType: String): Entity = + encodeToEntity(serializer(), value, entityType) diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt new file mode 100644 index 0000000..ac11421 --- /dev/null +++ b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt @@ -0,0 +1,39 @@ +package ru.mipt.npm.xodus.serialization.json + +import jetbrains.exodus.entitystore.PersistentEntityStores +import kotlinx.datetime.Instant +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.magix.api.MagixMessage +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.names.Name +import java.nio.file.Paths + +internal fun main() { + val expectedMessage = MagixMessage( + "dataforge", + "dataforge", + PropertyChangedMessage( + "acceleration", + Meta { + "x" put 3.0 + "y" put 9.0 + }, + Name.parse("virtual-car"), + Name.parse("magix-virtual-car"), + time = Instant.fromEpochMilliseconds(1337) + ), + "magix-virtual-car", + user = JsonObject(content = mapOf(Pair("name", JsonPrimitive("SCADA")))) + ) + + val entityStore = PersistentEntityStores.newInstance(Paths.get("xodus_serialization").toString()) + entityStore.executeInTransaction { txn -> + txn.encodeToEntity(expectedMessage, "MagixMessage") + } + + entityStore.executeInTransaction { txn -> + txn.getAll("MagixMessage").first?.let { println(txn.decodeFromEntity>(it) == expectedMessage) } + } +} \ No newline at end of file From e793cdefeeb57f475f9cacd7666b82db980a1355 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Fri, 3 Dec 2021 23:55:43 +0300 Subject: [PATCH 29/74] Add more extensions for encoder and decoder --- .../npm/xodus/serialization/json/decoder.kt | 19 +++++++++++++++++++ .../npm/xodus/serialization/json/encoder.kt | 11 +++++++++++ .../mipt/npm/xodus/serialization/json/main.kt | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt index 38e4a16..ff29904 100644 --- a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt +++ b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt @@ -1,6 +1,8 @@ package ru.mipt.npm.xodus.serialization.json import jetbrains.exodus.entitystore.Entity +import jetbrains.exodus.entitystore.EntityId +import jetbrains.exodus.entitystore.PersistentEntityStore import jetbrains.exodus.entitystore.StoreTransaction import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.json.* @@ -40,3 +42,20 @@ public fun StoreTransaction.decodeFromEntity(entity: Entity, deserializer: D } public inline fun StoreTransaction.decodeFromEntity(entity: Entity): T = decodeFromEntity(entity, serializer()) + +// First entity with entityType will be decoded +public fun PersistentEntityStore.decodeFromEntity(entityType: String, deserializer: DeserializationStrategy): T? { + return computeInTransaction { txn -> + txn.getAll(entityType).first?.let { txn.decodeFromEntity(it, deserializer) } + } +} + +public inline fun PersistentEntityStore.decodeFromEntity(entityType: String): T? = decodeFromEntity(entityType, serializer()) + +public fun PersistentEntityStore.decodeFromEntity(entityId: EntityId, deserializer: DeserializationStrategy): T? { + return computeInTransaction { txn -> + txn.decodeFromEntity(txn.getEntity(entityId), deserializer) + } +} + +public inline fun PersistentEntityStore.decodeFromEntity(entityId: EntityId): T? = decodeFromEntity(entityId, serializer()) diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt index 87173f9..e2dbaa3 100644 --- a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt +++ b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt @@ -1,6 +1,8 @@ package ru.mipt.npm.xodus.serialization.json import jetbrains.exodus.entitystore.Entity +import jetbrains.exodus.entitystore.EntityId +import jetbrains.exodus.entitystore.PersistentEntityStore import jetbrains.exodus.entitystore.StoreTransaction import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.json.* @@ -56,3 +58,12 @@ public fun StoreTransaction.encodeToEntity(serializer: SerializationStrategy public inline fun StoreTransaction.encodeToEntity(value: T, entityType: String): Entity = encodeToEntity(serializer(), value, entityType) + +public fun PersistentEntityStore.encodeToEntity(serializer: SerializationStrategy, value: T, entityType: String): EntityId { + return computeInTransaction { txn -> + txn.encodeToEntity(serializer, value, entityType).id + } +} + +public inline fun PersistentEntityStore.encodeToEntity(value: T, entityType: String): EntityId = + encodeToEntity(serializer(), value, entityType) diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt index ac11421..bec4b2d 100644 --- a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt +++ b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt @@ -28,7 +28,7 @@ internal fun main() { user = JsonObject(content = mapOf(Pair("name", JsonPrimitive("SCADA")))) ) - val entityStore = PersistentEntityStores.newInstance(Paths.get("xodus_serialization").toString()) + val entityStore = PersistentEntityStores.newInstance(Paths.get(".xodus_serialization").toString()) entityStore.executeInTransaction { txn -> txn.encodeToEntity(expectedMessage, "MagixMessage") } From cfd7bce2f3fdb662794cf08b3eb189acc8ae2e52 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Sat, 4 Dec 2021 00:35:12 +0300 Subject: [PATCH 30/74] Add some tests for encoding/decoding --- .../npm/xodus/serialization/json/encoder.kt | 2 + .../mipt/npm/xodus/serialization/json/main.kt | 3 +- .../serialization/json/EncoderDecoderTests.kt | 105 ++++++++++++++++++ 3 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 xodus-serialization/src/test/kotlin/ru/mipt/npm/xodus/serialization/json/EncoderDecoderTests.kt diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt index e2dbaa3..3cd5b79 100644 --- a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt +++ b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt @@ -16,6 +16,7 @@ internal fun StoreTransaction.encodeToEntity(jsonElement: JsonElement, entity: E jsonElement.forEach { entry -> entry.value.let { value -> when(value) { + // не сможем десериализовать, если JsonNull (надо ли обрабатывать???) (можно сохранить в отдельный список ключи null-ов) is JsonPrimitive -> { if (value.isString) { entity.setProperty(entry.key, value.content) @@ -30,6 +31,7 @@ internal fun StoreTransaction.encodeToEntity(jsonElement: JsonElement, entity: E } // считаем, что все элементы массива - JsonObject, иначе не можем напрямую сериализовать (надо придывать костыли???) + // не сможем десериализовать, если массив пустой (надо ли обрабатывать???) (можно сохранять в отдельный список ключи пустых массивов) is JsonArray -> { value.forEach { element -> val childEntity = newEntity("${entity.type}.${entry.key}") diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt index bec4b2d..60d9bb8 100644 --- a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt +++ b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt @@ -24,8 +24,7 @@ internal fun main() { Name.parse("magix-virtual-car"), time = Instant.fromEpochMilliseconds(1337) ), - "magix-virtual-car", - user = JsonObject(content = mapOf(Pair("name", JsonPrimitive("SCADA")))) + "magix-virtual-car" ) val entityStore = PersistentEntityStores.newInstance(Paths.get(".xodus_serialization").toString()) diff --git a/xodus-serialization/src/test/kotlin/ru/mipt/npm/xodus/serialization/json/EncoderDecoderTests.kt b/xodus-serialization/src/test/kotlin/ru/mipt/npm/xodus/serialization/json/EncoderDecoderTests.kt new file mode 100644 index 0000000..ad35069 --- /dev/null +++ b/xodus-serialization/src/test/kotlin/ru/mipt/npm/xodus/serialization/json/EncoderDecoderTests.kt @@ -0,0 +1,105 @@ +package ru.mipt.npm.xodus.serialization.json + +import jetbrains.exodus.entitystore.PersistentEntityStores +import kotlinx.datetime.Instant +import kotlinx.serialization.json.* +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.magix.api.MagixMessage +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.names.Name +import java.nio.file.Paths +import kotlin.test.assertEquals + +internal class EncoderDecoderTests { + companion object { + private val storePath = Paths.get(".xodus_serialization_test") + private val entityStore = PersistentEntityStores.newInstance(storePath.toString()) + + @AfterAll + @JvmStatic + fun deleteDatabase() { + entityStore.close() + storePath.toFile().deleteRecursively() + } + + @AfterEach + fun clearDatabase() { + entityStore.clear() + } + + fun checkEncodingDecodingCorrectness(json: JsonObject) { + val id = entityStore.encodeToEntity(json, "JsonObject") + assertEquals(json, entityStore.decodeFromEntity(id)) + } + + fun checkEncodingDecodingCorrectness(jsons: List) = jsons.forEach { + checkEncodingDecodingCorrectness(it) + } + + } + + @Test + fun `encoder throw Illegal exception if input is not a JsonObject`() { + assertThrows { + val json = JsonPrimitive(0) + entityStore.encodeToEntity(json, "JsonPrimitive") + } + + assertThrows { + val json = buildJsonArray {} + entityStore.encodeToEntity(json, "JsonArray") + } + } + + @Test + fun `correctly work with underlying JsonPrimitive`() { + val jsonLong = buildJsonObject { put("value", 0) } + val jsonDouble = buildJsonObject { put("value", 0.0) } + val jsonBoolean = buildJsonObject { put("value", true) } + val jsonString = buildJsonObject { put("value", "") } + + checkEncodingDecodingCorrectness(listOf(jsonLong, jsonDouble, jsonBoolean, jsonString)) + } + + @Test + fun `correctly work with underlying JsonArray`() { + checkEncodingDecodingCorrectness(buildJsonObject { putJsonArray("value") { + add(buildJsonObject { put("value", 0) }) + add(buildJsonObject { put("value", 0.0) }) + } }) + } + + @Test + fun `correctly work with underlying JsonObject`() { + checkEncodingDecodingCorrectness(buildJsonObject { + putJsonObject("value", { put("value", true) }) + }) + } + + @Test + fun testMagixMessagePropertyChangedMessage() { + val expectedMessage = MagixMessage( + "dataforge", + "dataforge", + PropertyChangedMessage( + "acceleration", + Meta { + "x" put 3.0 + "y" put 9.0 + }, + Name.parse("virtual-car"), + Name.parse("magix-virtual-car"), + time = Instant.fromEpochMilliseconds(1337) + ), + "magix-virtual-car", + user = buildJsonObject { put("name", "SCADA") } + ) + + val id = entityStore.encodeToEntity(expectedMessage, "MagixMessage") + assertEquals(expectedMessage, entityStore.decodeFromEntity>(id)) + } +} \ No newline at end of file From b79ad40a9b4a1491c5d7325fcfda50d4245f427b Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Sat, 4 Dec 2021 00:52:22 +0300 Subject: [PATCH 31/74] Fix some issues --- .../main/kotlin/ru/mipt/npm/controls/xodus/converters.kt | 8 ++++---- .../kotlin/ru/mipt/npm/controls/xodus/util/queries.kt | 7 ++----- .../kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt | 2 +- .../ru/mipt/npm/controls/demo/car/VirtualCarController.kt | 5 +++-- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt index bc7deb6..f7bbd87 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt @@ -13,7 +13,7 @@ import space.kscience.dataforge.names.Name import space.kscience.dataforge.values.Value import space.kscience.dataforge.values.ValueType -public fun PropertyChangedMessage.toEntity(transaction: StoreTransaction): Entity { +internal fun PropertyChangedMessage.toEntity(transaction: StoreTransaction): Entity { val entity = transaction.newEntity("PropertyChangedMessage") entity.setProperty("property", property) entity.setProperty("value", value.toString()) @@ -24,7 +24,7 @@ public fun PropertyChangedMessage.toEntity(transaction: StoreTransaction): Entit return entity } -public fun Entity.toPropertyChangedMessage(): PropertyChangedMessage? { +internal fun Entity.toPropertyChangedMessage(): PropertyChangedMessage? { if (getProperty("property") == null || getProperty("value") == null || getProperty("sourceDevice") == null) { return null } @@ -39,7 +39,7 @@ public fun Entity.toPropertyChangedMessage(): PropertyChangedMessage? { ) } -public fun MagixMessage.toEntity(transaction: StoreTransaction): Entity { +internal fun MagixMessage.toEntity(transaction: StoreTransaction): Entity { val entity = transaction.newEntity("MagixMessage") entity.setProperty("format", format) entity.setProperty("origin", origin) @@ -54,7 +54,7 @@ public fun MagixMessage.toEntity(transaction: StoreTransaction): Entity { return entity } -public fun Entity.toMagixMessage(): MagixMessage? { +internal fun Entity.toMagixMessage(): MagixMessage? { if (getProperty("format") == null || getProperty("origin") == null) { return null } diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt index f547e05..ee9361f 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt @@ -5,11 +5,8 @@ import kotlinx.datetime.Instant import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.xodus.toPropertyChangedMessage -//selectDeviceMessagesInRange -public fun StoreTransaction.fromTo( - range: ClosedRange, -// from: Instant, -// to: Instant, +public fun StoreTransaction.selectPropertyChangedMessagesFromRange( + range: ClosedRange ): List = find( "PropertyChangedMessage", "time", diff --git a/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt b/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt index 73d915b..b09ae2e 100644 --- a/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt +++ b/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt @@ -55,7 +55,7 @@ internal class QueriesTest { @Test fun testFromTo() { assertEquals(propertyChangedMessages.subList(0, 2).toSet(), entityStore.computeInReadonlyTransaction { - it.fromTo( Instant.fromEpochMilliseconds(1000)..Instant.fromEpochMilliseconds(1500)) + it.selectPropertyChangedMessagesFromRange( Instant.fromEpochMilliseconds(1000)..Instant.fromEpochMilliseconds(1500)) }.toSet()) } diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 8a652bb..31f315c 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -26,6 +26,7 @@ import ru.mipt.npm.magix.server.startMagixServer import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta import tornadofx.* +import java.nio.file.Paths class VirtualCarController : Controller(), ContextAware { @@ -49,12 +50,12 @@ class VirtualCarController : Controller(), ContextAware { virtualCar = deviceManager.install("virtual-car", VirtualCar) //starting magix event loop and connect it to entity store - magixEntityStore = PersistentEntityStores.newInstance("/home/marvel1337/2021/SCADA/.server_messages") + magixEntityStore = PersistentEntityStores.newInstance(Paths.get(".server_messages").toFile()) magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> flow.storeInXodus(magixEntityStore as PersistentEntityStore) } magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) - deviceEntityStore = PersistentEntityStores.newInstance("/home/marvel1337/2021/SCADA/.messages") + deviceEntityStore = PersistentEntityStores.newInstance(Paths.get(".messages").toFile()) //connect to device entity store xodusStorageJob = deviceManager.connectXodus(deviceEntityStore as PersistentEntityStore) //Create mongo client and connect to MongoDB From e07780e7da570b16e131ec3f39c84b960fe5efc6 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Sat, 4 Dec 2021 02:10:10 +0300 Subject: [PATCH 32/74] Add entityStoreFactory and refactor mongo/xodus connections.kt --- .../ru/mipt/npm/controls/mongo/connections.kt | 41 +++++++---- .../ru/mipt/npm/controls/xodus/connections.kt | 69 +++++++++++++++---- .../controls/demo/car/VirtualCarController.kt | 38 +++++----- 3 files changed, 102 insertions(+), 46 deletions(-) diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt index d5157c1..fded873 100644 --- a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt +++ b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt @@ -1,5 +1,6 @@ package ru.mipt.npm.controls.mongo +import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn @@ -16,30 +17,42 @@ import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.hubMessageFlow import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.context.debug +import space.kscience.dataforge.context.logger import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string -public object MongoClientFactory : Factory { - public const val connectionString: String = "mongodb://mongoadmin:secret@localhost:27888" +internal object DefaultMongoConfig { + const val databaseName = "deviceMessage" +} - override fun invoke(meta: Meta, context: Context): CoroutineClient = meta["connectionString"]?.string?.let { +public object MongoClientFactory : Factory { + private const val connectionString: String = "mongodb://mongoadmin:secret@localhost:27888" + + override fun invoke(meta: Meta, context: Context): CoroutineClient = meta["mongoConfig"]?.get("connectionString")?.string?.let { KMongo.createClient(it).coroutine } ?: KMongo.createClient(connectionString).coroutine } +@OptIn(InternalCoroutinesApi::class) public fun DeviceManager.connectMongo( - client: CoroutineClient, + factory: Factory, filterCondition: suspend (DeviceMessage) -> Boolean = { true } -): Job = hubMessageFlow(context).filter(filterCondition).onEach { message -> - context.launch { - client - .getDatabase("deviceServer") - .getCollection() - .insertOne(Json.encodeToString(message)) - } -}.launchIn(context).apply { - invokeOnCompletion { - client.close() +): Job { + val client = factory.invoke(meta, context) + logger.debug { "Mongo client opened" } + val collection = client + .getDatabase(meta["mongoConfig"]?.get("databaseName")?.string ?: DefaultMongoConfig.databaseName) + .getCollection() + return hubMessageFlow(context).filter(filterCondition).onEach { message -> + context.launch { + collection.insertOne(Json.encodeToString(message)) + } + }.launchIn(context).apply { + invokeOnCompletion(onCancelling = true) { + logger.debug { "Mongo client closed" } + client.close() + } } } diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt index 4aa1be6..b1cbf09 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt @@ -1,26 +1,57 @@ package ru.mipt.npm.controls.xodus +import io.ktor.application.* import jetbrains.exodus.entitystore.PersistentEntityStore +import jetbrains.exodus.entitystore.PersistentEntityStores +import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.job import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.hubMessageFlow import ru.mipt.npm.magix.server.GenericMagixMessage +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.context.debug +import space.kscience.dataforge.context.logger +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.string +import java.nio.file.Paths +internal object DefaultXodusConfig { + val entityStorePath = Paths.get(".messages") +} -public fun DeviceManager.connectXodus( - entityStore: PersistentEntityStore, - filterCondition: suspend (DeviceMessage) -> Boolean = { it is PropertyChangedMessage } -): Job = hubMessageFlow(context).filter(filterCondition).onEach { message -> - entityStore.executeInTransaction { - (message as PropertyChangedMessage).toEntity(it) +public object EntityStoreFactory : Factory { + override fun invoke(meta: Meta, context: Context): PersistentEntityStore { + return PersistentEntityStores.newInstance( + meta["xodusConfig"]?.get("entityStorePath")?.string ?: DefaultXodusConfig.entityStorePath.toString() + ) } -}.launchIn(context) +} + +@OptIn(InternalCoroutinesApi::class) +public fun DeviceManager.connectXodus( + factory: Factory, + filterCondition: suspend (DeviceMessage) -> Boolean = { it is PropertyChangedMessage } +): Job { + val entityStore = factory.invoke(meta, context) + logger.debug { "Device entity store opened" } + + return hubMessageFlow(context).filter(filterCondition).onEach { message -> + entityStore.executeInTransaction { + (message as PropertyChangedMessage).toEntity(it) + } + }.launchIn(context).apply { + invokeOnCompletion(onCancelling = true) { + entityStore.close() + logger.debug { "Device entity store closed" } + } + } +} //public fun CoroutineScope.startMagixServer( // entityStore: PersistentEntityStore, @@ -52,4 +83,18 @@ public fun SharedFlow.storeInXodus( entity.setProperty("value", message.toString()) } } -} \ No newline at end of file +} + +@OptIn(InternalCoroutinesApi::class) +public fun Application.storeInXodus( + factory: Factory, + flow: MutableSharedFlow, + meta: Meta = Meta.EMPTY +) { + val entityStore = factory.invoke(meta) + + flow.storeInXodus(entityStore) + coroutineContext.job.invokeOnCompletion(onCancelling = true) { + entityStore.close() + } +} diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 31f315c..44aae10 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -6,11 +6,8 @@ import javafx.scene.Parent import javafx.scene.control.TextField import javafx.scene.layout.Priority import javafx.stage.Stage -import jetbrains.exodus.entitystore.PersistentEntityStore -import jetbrains.exodus.entitystore.PersistentEntityStores import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import org.litote.kmongo.coroutine.CoroutineClient import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.client.connectToMagix import ru.mipt.npm.controls.controllers.DeviceManager @@ -18,6 +15,7 @@ import ru.mipt.npm.controls.controllers.install import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration import ru.mipt.npm.controls.mongo.MongoClientFactory import ru.mipt.npm.controls.mongo.connectMongo +import ru.mipt.npm.controls.xodus.EntityStoreFactory import ru.mipt.npm.controls.xodus.connectXodus import ru.mipt.npm.controls.xodus.storeInXodus import ru.mipt.npm.magix.api.MagixEndpoint @@ -28,14 +26,16 @@ import space.kscience.dataforge.meta.Meta import tornadofx.* import java.nio.file.Paths +internal object VirtualCarControllerConfig { + val deviceEntityStorePath = Paths.get(".messages") + val magixEntityStorePath = Paths.get(".server_messages") +} + class VirtualCarController : Controller(), ContextAware { var virtualCar: VirtualCar? = null var magixVirtualCar: MagixVirtualCar? = null var magixServer: ApplicationEngine? = null - var deviceEntityStore: PersistentEntityStore? = null - var magixEntityStore: PersistentEntityStore? = null - var mongoClient: CoroutineClient? = null var xodusStorageJob: Job? = null var mongoStorageJob: Job? = null @@ -43,25 +43,29 @@ class VirtualCarController : Controller(), ContextAware { plugin(DeviceManager) } - private val deviceManager = context.fetch(DeviceManager) + private val deviceManager = context.fetch(DeviceManager, Meta { + "xodusConfig" put { + "entityStorePath" put VirtualCarControllerConfig.deviceEntityStorePath.toString() + } + }) fun init() { context.launch { virtualCar = deviceManager.install("virtual-car", VirtualCar) //starting magix event loop and connect it to entity store - magixEntityStore = PersistentEntityStores.newInstance(Paths.get(".server_messages").toFile()) magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> - flow.storeInXodus(magixEntityStore as PersistentEntityStore) + storeInXodus(EntityStoreFactory, flow, Meta { + "xodusConfig" put { + "entityStorePath" put VirtualCarControllerConfig.magixEntityStorePath.toString() + } + }) } magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) - deviceEntityStore = PersistentEntityStores.newInstance(Paths.get(".messages").toFile()) //connect to device entity store - xodusStorageJob = deviceManager.connectXodus(deviceEntityStore as PersistentEntityStore) + xodusStorageJob = deviceManager.connectXodus(EntityStoreFactory) //Create mongo client and connect to MongoDB - val mongoClient = MongoClientFactory.invoke(meta = Meta.EMPTY, context) - mongoStorageJob = deviceManager.connectMongo(mongoClient) - this@VirtualCarController.mongoClient = mongoClient + mongoStorageJob = deviceManager.connectMongo(MongoClientFactory) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) deviceManager.connectToMagix(deviceEndpoint) @@ -76,12 +80,6 @@ class VirtualCarController : Controller(), ContextAware { logger.info { "Magix virtual car server stopped" } virtualCar?.close() logger.info { "Virtual car server stopped" } - deviceEntityStore?.close() - logger.info { "Device entity store closed" } - magixEntityStore?.close() - logger.info { "Magix entity store closed" } - mongoClient?.close() - logger.info { "MongoClient closed" } context.close() } } From f880b2d63746788428af7a51955989ea86921dff Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Sat, 4 Dec 2021 02:15:57 +0300 Subject: [PATCH 33/74] Move to xodus-serialization --- controls-xodus/build.gradle.kts | 1 + .../kotlin/ru/mipt/npm/controls/xodus/connections.kt | 12 ++++-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/controls-xodus/build.gradle.kts b/controls-xodus/build.gradle.kts index cf06c4c..9421f36 100644 --- a/controls-xodus/build.gradle.kts +++ b/controls-xodus/build.gradle.kts @@ -10,6 +10,7 @@ dependencies { implementation(projects.magix.magixApi) implementation(projects.controlsMagixClient) implementation(projects.magix.magixServer) + implementation(projects.xodusSerialization) implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") implementation("org.jetbrains.xodus:xodus-environment:$xodusVersion") implementation("org.jetbrains.xodus:xodus-vfs:$xodusVersion") diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt index b1cbf09..126d983 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt @@ -12,6 +12,7 @@ import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.hubMessageFlow import ru.mipt.npm.magix.server.GenericMagixMessage +import ru.mipt.npm.xodus.serialization.json.encodeToEntity import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.context.debug @@ -36,15 +37,13 @@ public object EntityStoreFactory : Factory { @OptIn(InternalCoroutinesApi::class) public fun DeviceManager.connectXodus( factory: Factory, - filterCondition: suspend (DeviceMessage) -> Boolean = { it is PropertyChangedMessage } + filterCondition: suspend (DeviceMessage) -> Boolean = { true } ): Job { val entityStore = factory.invoke(meta, context) logger.debug { "Device entity store opened" } return hubMessageFlow(context).filter(filterCondition).onEach { message -> - entityStore.executeInTransaction { - (message as PropertyChangedMessage).toEntity(it) - } + entityStore.encodeToEntity(message, "DeviceMessage") }.launchIn(context).apply { invokeOnCompletion(onCancelling = true) { entityStore.close() @@ -78,10 +77,7 @@ public fun SharedFlow.storeInXodus( flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, ){ filter(flowFilter).onEach { message -> - entityStore.executeInTransaction { txn -> - val entity = txn.newEntity("MagixMessage") - entity.setProperty("value", message.toString()) - } + entityStore.encodeToEntity(message, "MagixMessage") } } From 76846a50cd9b08512f5ff46e8e5210c21f751204 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 11 Dec 2021 17:59:19 +0300 Subject: [PATCH 34/74] Some refactoring --- build.gradle.kts | 2 +- .../ru/mipt/npm/controls/xodus/connections.kt | 47 ++++++++++--------- .../controls/demo/car/VirtualCarController.kt | 7 ++- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 4 +- xodus-serialization/build.gradle.kts | 2 + 6 files changed, 34 insertions(+), 30 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6d7df71..9529d0d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.gradle.project") } -val dataforgeVersion: String by extra("0.5.1") +val dataforgeVersion: String by extra("0.5.2") val ktorVersion: String by extra(ru.mipt.npm.gradle.KScienceVersions.ktorVersion) val rsocketVersion by extra("0.13.1") diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt index 126d983..02d5805 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt @@ -1,6 +1,6 @@ package ru.mipt.npm.controls.xodus -import io.ktor.application.* +import io.ktor.application.Application import jetbrains.exodus.entitystore.PersistentEntityStore import jetbrains.exodus.entitystore.PersistentEntityStores import kotlinx.coroutines.InternalCoroutinesApi @@ -8,7 +8,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.* import kotlinx.coroutines.job import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.hubMessageFlow import ru.mipt.npm.magix.server.GenericMagixMessage @@ -20,26 +19,29 @@ import space.kscience.dataforge.context.logger import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string -import java.nio.file.Paths +import space.kscience.dataforge.names.Name -internal object DefaultXodusConfig { - val entityStorePath = Paths.get(".messages") +private const val DEFAULT_XODUS_STORE_PATH = ".storage" +private val XODUS_STORE_PROPERTY = Name.of("xodus", "entityStorePath") + +private fun Context.getPersistentEntityStore(meta: Meta = Meta.EMPTY): PersistentEntityStore { + val storePath = meta[XODUS_STORE_PROPERTY]?.string + ?: properties[XODUS_STORE_PROPERTY]?.string + ?: DEFAULT_XODUS_STORE_PATH + + return PersistentEntityStores.newInstance(storePath) } -public object EntityStoreFactory : Factory { - override fun invoke(meta: Meta, context: Context): PersistentEntityStore { - return PersistentEntityStores.newInstance( - meta["xodusConfig"]?.get("entityStorePath")?.string ?: DefaultXodusConfig.entityStorePath.toString() - ) - } +internal val defaultPersistentStoreFactory = object : Factory { + override fun invoke(meta: Meta, context: Context): PersistentEntityStore = context.getPersistentEntityStore(meta) } @OptIn(InternalCoroutinesApi::class) -public fun DeviceManager.connectXodus( - factory: Factory, - filterCondition: suspend (DeviceMessage) -> Boolean = { true } +public fun DeviceManager.storeMessagesInXodus( + factory: Factory = defaultPersistentStoreFactory, + filterCondition: suspend (DeviceMessage) -> Boolean = { true }, ): Job { - val entityStore = factory.invoke(meta, context) + val entityStore = factory(Meta.EMPTY, context) logger.debug { "Device entity store opened" } return hubMessageFlow(context).filter(filterCondition).onEach { message -> @@ -72,10 +74,10 @@ public fun DeviceManager.connectXodus( // } //} -public fun SharedFlow.storeInXodus( +internal fun Flow.storeInXodus( entityStore: PersistentEntityStore, - flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, -){ + flowFilter: (GenericMagixMessage) -> Boolean = { true }, +) { filter(flowFilter).onEach { message -> entityStore.encodeToEntity(message, "MagixMessage") } @@ -83,13 +85,14 @@ public fun SharedFlow.storeInXodus( @OptIn(InternalCoroutinesApi::class) public fun Application.storeInXodus( - factory: Factory, flow: MutableSharedFlow, - meta: Meta = Meta.EMPTY + meta: Meta = Meta.EMPTY, + factory: Factory = defaultPersistentStoreFactory, + flowFilter: (GenericMagixMessage) -> Boolean = { true }, ) { - val entityStore = factory.invoke(meta) + val entityStore = factory(meta) - flow.storeInXodus(entityStore) + flow.storeInXodus(entityStore, flowFilter) coroutineContext.job.invokeOnCompletion(onCancelling = true) { entityStore.close() } diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 44aae10..5ab4372 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -15,9 +15,8 @@ import ru.mipt.npm.controls.controllers.install import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration import ru.mipt.npm.controls.mongo.MongoClientFactory import ru.mipt.npm.controls.mongo.connectMongo -import ru.mipt.npm.controls.xodus.EntityStoreFactory -import ru.mipt.npm.controls.xodus.connectXodus import ru.mipt.npm.controls.xodus.storeInXodus +import ru.mipt.npm.controls.xodus.storeMessagesInXodus import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.rsocket.rSocketWithTcp import ru.mipt.npm.magix.server.startMagixServer @@ -55,7 +54,7 @@ class VirtualCarController : Controller(), ContextAware { //starting magix event loop and connect it to entity store magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> - storeInXodus(EntityStoreFactory, flow, Meta { + storeInXodus( flow, Meta { "xodusConfig" put { "entityStorePath" put VirtualCarControllerConfig.magixEntityStorePath.toString() } @@ -63,7 +62,7 @@ class VirtualCarController : Controller(), ContextAware { } magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) //connect to device entity store - xodusStorageJob = deviceManager.connectXodus(EntityStoreFactory) + xodusStorageJob = deviceManager.storeMessagesInXodus() //Create mongo client and connect to MongoDB mongoStorageJob = deviceManager.connectMongo(MongoClientFactory) //Launch device client and connect it to the server diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..e750102 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index 896d04d..a1406e3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,7 +5,7 @@ enableFeaturePreview("VERSION_CATALOGS") pluginManagement { - val toolsVersion = "0.10.5" + val toolsVersion = "0.10.7" repositories { maven("https://repo.kotlin.link") @@ -29,7 +29,7 @@ dependencyResolutionManagement { versionCatalogs { create("npm") { - from("ru.mipt.npm:version-catalog:0.10.5") + from("ru.mipt.npm:version-catalog:0.10.7") } } } diff --git a/xodus-serialization/build.gradle.kts b/xodus-serialization/build.gradle.kts index dad94f5..40755c2 100644 --- a/xodus-serialization/build.gradle.kts +++ b/xodus-serialization/build.gradle.kts @@ -5,6 +5,8 @@ plugins { val xodusVersion = "1.3.232" +//TODO to be moved to DataForge + kscience { useSerialization { json() From dec45b60505ca9ba849ba18633fb69d55bf3b168 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Thu, 16 Dec 2021 16:54:48 +0300 Subject: [PATCH 35/74] Make this work --- build.gradle.kts | 2 +- .../main/kotlin/ru/mipt/npm/controls/xodus/connections.kt | 8 ++++---- .../ru/mipt/npm/controls/demo/car/VirtualCarController.kt | 5 ++--- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 4 ++-- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9529d0d..6d7df71 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.gradle.project") } -val dataforgeVersion: String by extra("0.5.2") +val dataforgeVersion: String by extra("0.5.1") val ktorVersion: String by extra(ru.mipt.npm.gradle.KScienceVersions.ktorVersion) val rsocketVersion by extra("0.13.1") diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt index 02d5805..8c72785 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt @@ -22,7 +22,7 @@ import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name private const val DEFAULT_XODUS_STORE_PATH = ".storage" -private val XODUS_STORE_PROPERTY = Name.of("xodus", "entityStorePath") +public val XODUS_STORE_PROPERTY: Name = Name.of("xodus", "entityStorePath") private fun Context.getPersistentEntityStore(meta: Meta = Meta.EMPTY): PersistentEntityStore { val storePath = meta[XODUS_STORE_PROPERTY]?.string @@ -41,7 +41,7 @@ public fun DeviceManager.storeMessagesInXodus( factory: Factory = defaultPersistentStoreFactory, filterCondition: suspend (DeviceMessage) -> Boolean = { true }, ): Job { - val entityStore = factory(Meta.EMPTY, context) + val entityStore = factory(meta, context) logger.debug { "Device entity store opened" } return hubMessageFlow(context).filter(filterCondition).onEach { message -> @@ -76,7 +76,7 @@ public fun DeviceManager.storeMessagesInXodus( internal fun Flow.storeInXodus( entityStore: PersistentEntityStore, - flowFilter: (GenericMagixMessage) -> Boolean = { true }, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, ) { filter(flowFilter).onEach { message -> entityStore.encodeToEntity(message, "MagixMessage") @@ -88,7 +88,7 @@ public fun Application.storeInXodus( flow: MutableSharedFlow, meta: Meta = Meta.EMPTY, factory: Factory = defaultPersistentStoreFactory, - flowFilter: (GenericMagixMessage) -> Boolean = { true }, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, ) { val entityStore = factory(meta) diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 5ab4372..03b62f1 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -15,6 +15,7 @@ import ru.mipt.npm.controls.controllers.install import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration import ru.mipt.npm.controls.mongo.MongoClientFactory import ru.mipt.npm.controls.mongo.connectMongo +import ru.mipt.npm.controls.xodus.XODUS_STORE_PROPERTY import ru.mipt.npm.controls.xodus.storeInXodus import ru.mipt.npm.controls.xodus.storeMessagesInXodus import ru.mipt.npm.magix.api.MagixEndpoint @@ -55,9 +56,7 @@ class VirtualCarController : Controller(), ContextAware { //starting magix event loop and connect it to entity store magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> storeInXodus( flow, Meta { - "xodusConfig" put { - "entityStorePath" put VirtualCarControllerConfig.magixEntityStorePath.toString() - } + XODUS_STORE_PROPERTY put VirtualCarControllerConfig.magixEntityStorePath.toString() }) } magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102..ffed3a2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index a1406e3..896d04d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,7 +5,7 @@ enableFeaturePreview("VERSION_CATALOGS") pluginManagement { - val toolsVersion = "0.10.7" + val toolsVersion = "0.10.5" repositories { maven("https://repo.kotlin.link") @@ -29,7 +29,7 @@ dependencyResolutionManagement { versionCatalogs { create("npm") { - from("ru.mipt.npm:version-catalog:0.10.7") + from("ru.mipt.npm:version-catalog:0.10.5") } } } From 0c4d2fc9e1b221dc35da01abb9be7b98656d74da Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Thu, 16 Dec 2021 17:25:35 +0300 Subject: [PATCH 36/74] Add storeInMongo for magix server --- controls-mongo/build.gradle.kts | 1 + .../ru/mipt/npm/controls/mongo/connections.kt | 55 +++++++++++++++---- .../controls/demo/car/VirtualCarController.kt | 6 +- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/controls-mongo/build.gradle.kts b/controls-mongo/build.gradle.kts index e6ce49d..624b71f 100644 --- a/controls-mongo/build.gradle.kts +++ b/controls-mongo/build.gradle.kts @@ -9,5 +9,6 @@ dependencies { implementation(projects.controlsCore) implementation(projects.magix.magixApi) implementation(projects.controlsMagixClient) + implementation(projects.magix.magixServer) implementation("org.litote.kmongo:kmongo-coroutine-serialization:$kmongoVersion") } diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt index fded873..a8a7800 100644 --- a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt +++ b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt @@ -1,20 +1,22 @@ package ru.mipt.npm.controls.mongo +import io.ktor.application.* import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.job import kotlinx.coroutines.launch import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import org.litote.kmongo.coroutine.CoroutineClient +import org.litote.kmongo.coroutine.CoroutineCollection import org.litote.kmongo.coroutine.coroutine import org.litote.kmongo.coroutine.insertOne import org.litote.kmongo.reactivestreams.KMongo import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.hubMessageFlow +import ru.mipt.npm.magix.server.GenericMagixMessage import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.context.debug @@ -22,17 +24,19 @@ import space.kscience.dataforge.context.logger import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string +import space.kscience.dataforge.names.Name -internal object DefaultMongoConfig { - const val databaseName = "deviceMessage" -} +private const val DEFAULT_MONGO_DATABASE_URL = "mongodb://mongoadmin:secret@localhost:27888" +private const val DEFAULT_DEVICE_MESSAGE_DATABASE_NAME = "deviceMessage" +private const val DEFAULT_MAGIX_MESSAGE_DATABASE_NAME = "magixMessage" +public val MONGO_DATABASE_URL_PROPERTY: Name = Name.of("mongo", "databaseUrl") +public val MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY: Name = Name.of("mongo", "deviceMessageDatabaseName") +public val MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY: Name = Name.of("mongo", "magixMessageDatabaseName") -public object MongoClientFactory : Factory { - private const val connectionString: String = "mongodb://mongoadmin:secret@localhost:27888" - - override fun invoke(meta: Meta, context: Context): CoroutineClient = meta["mongoConfig"]?.get("connectionString")?.string?.let { +public object DefaultMongoClientFactory : Factory { + override fun invoke(meta: Meta, context: Context): CoroutineClient = meta[MONGO_DATABASE_URL_PROPERTY]?.string?.let { KMongo.createClient(it).coroutine - } ?: KMongo.createClient(connectionString).coroutine + } ?: KMongo.createClient(DEFAULT_MONGO_DATABASE_URL).coroutine } @OptIn(InternalCoroutinesApi::class) @@ -43,7 +47,7 @@ public fun DeviceManager.connectMongo( val client = factory.invoke(meta, context) logger.debug { "Mongo client opened" } val collection = client - .getDatabase(meta["mongoConfig"]?.get("databaseName")?.string ?: DefaultMongoConfig.databaseName) + .getDatabase(meta[MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY]?.string ?: DEFAULT_DEVICE_MESSAGE_DATABASE_NAME) .getCollection() return hubMessageFlow(context).filter(filterCondition).onEach { message -> context.launch { @@ -56,3 +60,30 @@ public fun DeviceManager.connectMongo( } } } + +internal fun Flow.storeInMongo( + collection: CoroutineCollection, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, +) { + filter(flowFilter).onEach { message -> + collection.insertOne(Json.encodeToString(message)) + } +} + +@OptIn(InternalCoroutinesApi::class) +public fun Application.storeInMongo( + flow: MutableSharedFlow, + meta: Meta = Meta.EMPTY, + factory: Factory = DefaultMongoClientFactory, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, +) { + val client = factory.invoke(meta) + val collection = client + .getDatabase(meta[MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY]?.string ?: DEFAULT_MAGIX_MESSAGE_DATABASE_NAME) + .getCollection() + + flow.storeInMongo(collection, flowFilter) + coroutineContext.job.invokeOnCompletion(onCancelling = true) { + client.close() + } +} diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 03b62f1..0dd6ffe 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -13,8 +13,9 @@ 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.car.IVirtualCar.Companion.acceleration -import ru.mipt.npm.controls.mongo.MongoClientFactory +import ru.mipt.npm.controls.mongo.DefaultMongoClientFactory import ru.mipt.npm.controls.mongo.connectMongo +import ru.mipt.npm.controls.mongo.storeInMongo import ru.mipt.npm.controls.xodus.XODUS_STORE_PROPERTY import ru.mipt.npm.controls.xodus.storeInXodus import ru.mipt.npm.controls.xodus.storeMessagesInXodus @@ -58,12 +59,13 @@ class VirtualCarController : Controller(), ContextAware { storeInXodus( flow, Meta { XODUS_STORE_PROPERTY put VirtualCarControllerConfig.magixEntityStorePath.toString() }) + storeInMongo(flow) } magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) //connect to device entity store xodusStorageJob = deviceManager.storeMessagesInXodus() //Create mongo client and connect to MongoDB - mongoStorageJob = deviceManager.connectMongo(MongoClientFactory) + mongoStorageJob = deviceManager.connectMongo(DefaultMongoClientFactory) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) deviceManager.connectToMagix(deviceEndpoint) From 476dbfdeafbc33f0881b9cefb8f74055ae2cbd38 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Thu, 16 Dec 2021 18:18:47 +0300 Subject: [PATCH 37/74] Add getPropertyHistory for xodus --- .../mipt/npm/controls/xodus/util/queries.kt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt index ee9361f..5d7f959 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt @@ -1,9 +1,14 @@ package ru.mipt.npm.controls.xodus.util +import jetbrains.exodus.entitystore.PersistentEntityStore import jetbrains.exodus.entitystore.StoreTransaction import kotlinx.datetime.Instant import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.xodus.defaultPersistentStoreFactory import ru.mipt.npm.controls.xodus.toPropertyChangedMessage +import ru.mipt.npm.xodus.serialization.json.decodeFromEntity +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.meta.Meta public fun StoreTransaction.selectPropertyChangedMessagesFromRange( range: ClosedRange @@ -13,3 +18,26 @@ public fun StoreTransaction.selectPropertyChangedMessagesFromRange( range.start.toEpochMilliseconds(), range.endInclusive.toEpochMilliseconds() ).mapNotNull { it.toPropertyChangedMessage() } + +/** + * @return the list of deviceMessages that describes changes of specified property of specified device sorted by time + * @param sourceDeviceName a name of device, history of which property we want to get + * @param propertyName a name of property, history of which we want to get + * @param factory a factory that produce mongo clients + */ +public fun getPropertyHistory( + sourceDeviceName: String, + propertyName: String, + factory: Factory = defaultPersistentStoreFactory, + meta: Meta = Meta.EMPTY +): List { + return factory(meta).use { store -> + store.computeInTransaction { txn -> + txn.find("DeviceMessage", "type", "property.changed").asSequence() + .filter { it?.getProperty("sourceDevice")?.let { it == sourceDeviceName } ?: false && + it?.getProperty("property")?.let { it == propertyName } ?: false + }.sortedByDescending { it?.getProperty("time")?.let { timeStr -> Instant.parse(timeStr as String) } } + .toList().map { txn.decodeFromEntity(it) } + } + } +} From 7ba36f2eb18bcc97fd7914259009bfc611ead714 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Thu, 16 Dec 2021 23:14:32 +0300 Subject: [PATCH 38/74] Add some docs --- controls-mongo/README.md | 26 +++++++++++++++++++ .../ru/mipt/npm/controls/mongo/connections.kt | 13 ++++++++++ controls-xodus/README.md | 26 +++++++++++++++++++ .../ru/mipt/npm/controls/xodus/connections.kt | 13 ++++++++++ 4 files changed, 78 insertions(+) create mode 100644 controls-mongo/README.md create mode 100644 controls-xodus/README.md diff --git a/controls-mongo/README.md b/controls-mongo/README.md new file mode 100644 index 0000000..a627574 --- /dev/null +++ b/controls-mongo/README.md @@ -0,0 +1,26 @@ +# Description +This module allows you to store [DeviceMessages](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt) +from certain [DeviceManager](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt) +or [MagixMessages](magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt) +from [magix server](/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt) +in [mongoDB](https://www.mongodb.com/). + +# Usage + +All usage examples can be found in [VirtualCarController](/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt). + +## Storage from Device Manager + +Just call storeMessagesInXodus. For more details, you can see comments in [source code](/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt) + +## Storage from Magix Server + +Just pass such lambda as parameter to startMagixServer: +```kotlin +{ flow -> + // some code + storeInMongo(flow) + // some code +} +``` +For more details, you can see comments in [source code](/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt) diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt index a8a7800..e041171 100644 --- a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt +++ b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt @@ -39,6 +39,13 @@ public object DefaultMongoClientFactory : Factory { } ?: KMongo.createClient(DEFAULT_MONGO_DATABASE_URL).coroutine } +/** + * Begin to store DeviceMessages from this DeviceManager + * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. + * DeviceManager's meta and context will be used for in invoke method. + * @param filterCondition allow you to specify messages which we want to store. Always true by default. + * @return Job which responsible for our storage + */ @OptIn(InternalCoroutinesApi::class) public fun DeviceManager.connectMongo( factory: Factory, @@ -70,6 +77,12 @@ internal fun Flow.storeInMongo( } } +/** Begin to store MagixMessages from certain flow + * @param flow flow of messages which we will store + * @param meta Meta which may have some configuration parameters for our storage and will be used in invoke method of factory + * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. + * @param flowFilter allow you to specify messages which we want to store. Always true by default. + */ @OptIn(InternalCoroutinesApi::class) public fun Application.storeInMongo( flow: MutableSharedFlow, diff --git a/controls-xodus/README.md b/controls-xodus/README.md new file mode 100644 index 0000000..dca332c --- /dev/null +++ b/controls-xodus/README.md @@ -0,0 +1,26 @@ +# Description +This module allows you to store [DeviceMessages](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt) +from certain [DeviceManager](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt) +or [MagixMessages](magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt) +from [magix server](/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt) +in [xodus database](https://github.com/JetBrains/xodus). + +# Usage + +All usage examples can be found in [VirtualCarController](/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt). + +## Storage from Device Manager + +Just call connectMongo. For more details, you can see comments in [source code](/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt) + +## Storage from Magix Server + +Just pass such lambda as parameter to startMagixServer: +```kotlin +{ flow -> + // some code + storeInXodus(flow) + // some code +} +``` +For more details, you can see comments in [source code](/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt) diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt index 8c72785..3fe3ce6 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt @@ -36,6 +36,13 @@ internal val defaultPersistentStoreFactory = object : Factory = defaultPersistentStoreFactory, @@ -83,6 +90,12 @@ internal fun Flow.storeInXodus( } } +/** Begin to store MagixMessages from certain flow + * @param flow flow of messages which we will store + * @param meta Meta which may have some configuration parameters for our storage and will be used in invoke method of factory + * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. + * @param flowFilter allow you to specify messages which we want to store. Always true by default. + */ @OptIn(InternalCoroutinesApi::class) public fun Application.storeInXodus( flow: MutableSharedFlow, From 29c0291fa3bdefb51ef469249599380f438f65b4 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 24 Dec 2021 20:52:04 +0300 Subject: [PATCH 39/74] Add details for device description message. Update build plugin and kotlin. --- .../ru/mipt/npm/controls/api/DeviceMessage.kt | 2 ++ .../controls/controllers/deviceMessages.kt | 21 +++---------------- .../mipt/npm/controls/demo/car/VirtualCar.kt | 3 ++- .../ru/mipt/npm/controls/demo/DemoDevice.kt | 4 ++-- gradle/wrapper/gradle-wrapper.properties | 2 +- .../pimotionmaster/PiMotionMasterDevice.kt | 3 ++- settings.gradle.kts | 4 ++-- 7 files changed, 14 insertions(+), 25 deletions(-) diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt index f919f1e..9d9bc19 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt @@ -113,6 +113,8 @@ public data class GetDescriptionMessage( @SerialName("description") public data class DescriptionMessage( val description: Meta, + val properties: Collection, + val actions: Collection, override val sourceDevice: Name, override val targetDevice: Name? = null, override val comment: String? = null, diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/deviceMessages.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/deviceMessages.kt index 5158961..07fa3cb 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/deviceMessages.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/deviceMessages.kt @@ -5,11 +5,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.encodeToJsonElement import ru.mipt.npm.controls.api.* -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.toMeta import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.plus @@ -48,21 +44,10 @@ public suspend fun Device.respondMessage(deviceTarget: Name, request: DeviceMess } is GetDescriptionMessage -> { - val descriptionMeta = Meta { - "properties" put { - propertyDescriptors.map { descriptor -> - descriptor.name put Json.encodeToJsonElement(descriptor).toMeta() - } - } - "actions" put { - actionDescriptors.map { descriptor -> - descriptor.name put Json.encodeToJsonElement(descriptor).toMeta() - } - } - } - DescriptionMessage( - description = descriptionMeta, + description = meta, + properties = propertyDescriptors, + actions = actionDescriptors, sourceDevice = deviceTarget, targetDevice = request.sourceDevice ) diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt index 2cfea25..18fc440 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt @@ -17,6 +17,7 @@ import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.transformations.MetaConverter import kotlin.math.pow import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds import kotlin.time.ExperimentalTime data class Vector2D(var x: Double = 0.0, var y: Double = 0.0) : MetaRepr { @@ -103,7 +104,7 @@ open class VirtualCar(context: Context, meta: Meta) : DeviceBySpec(I //initializing the clock timeState = Clock.System.now() //starting regular updates - doRecurring(Duration.milliseconds(100)) { + doRecurring(100.milliseconds) { update() } } diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt index d4a3912..9bd45cf 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt @@ -10,7 +10,7 @@ import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.transformations.MetaConverter import space.kscience.dataforge.values.ValueType import java.time.Instant -import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds import kotlin.time.ExperimentalTime @@ -27,7 +27,7 @@ class DemoDevice(context: Context, meta: Meta) : DeviceBySpec(DemoDe cosScale.read() timeScale.read() } - doRecurring(Duration.milliseconds(50)) { + doRecurring(50.milliseconds) { coordinates.read() } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..d2880ba 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists 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 2f43e62..7bdfa9f 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 @@ -25,6 +25,7 @@ import space.kscience.dataforge.values.asValue import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds class PiMotionMasterDevice( context: Context, @@ -42,7 +43,7 @@ class PiMotionMasterDevice( } } - var timeoutValue: Duration = Duration.microseconds(200) + var timeoutValue: Duration = 200.milliseconds /** * Name-friendly accessor for axis diff --git a/settings.gradle.kts b/settings.gradle.kts index 198eaff..f67c59d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,7 +5,7 @@ enableFeaturePreview("VERSION_CATALOGS") pluginManagement { - val toolsVersion = "0.10.5" + val toolsVersion = "0.10.7" repositories { maven("https://repo.kotlin.link") @@ -29,7 +29,7 @@ dependencyResolutionManagement { versionCatalogs { create("npm") { - from("ru.mipt.npm:version-catalog:0.10.5") + from("ru.mipt.npm:version-catalog:0.10.7") } } } From 0c7cd951aae6537415e9a9843d4733da7b1ac0e3 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Thu, 16 Dec 2021 16:54:48 +0300 Subject: [PATCH 40/74] Make this work --- build.gradle.kts | 2 +- .../main/kotlin/ru/mipt/npm/controls/xodus/connections.kt | 8 ++++---- .../ru/mipt/npm/controls/demo/car/VirtualCarController.kt | 5 ++--- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 4 ++-- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9529d0d..6d7df71 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.gradle.project") } -val dataforgeVersion: String by extra("0.5.2") +val dataforgeVersion: String by extra("0.5.1") val ktorVersion: String by extra(ru.mipt.npm.gradle.KScienceVersions.ktorVersion) val rsocketVersion by extra("0.13.1") diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt index 02d5805..8c72785 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt @@ -22,7 +22,7 @@ import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name private const val DEFAULT_XODUS_STORE_PATH = ".storage" -private val XODUS_STORE_PROPERTY = Name.of("xodus", "entityStorePath") +public val XODUS_STORE_PROPERTY: Name = Name.of("xodus", "entityStorePath") private fun Context.getPersistentEntityStore(meta: Meta = Meta.EMPTY): PersistentEntityStore { val storePath = meta[XODUS_STORE_PROPERTY]?.string @@ -41,7 +41,7 @@ public fun DeviceManager.storeMessagesInXodus( factory: Factory = defaultPersistentStoreFactory, filterCondition: suspend (DeviceMessage) -> Boolean = { true }, ): Job { - val entityStore = factory(Meta.EMPTY, context) + val entityStore = factory(meta, context) logger.debug { "Device entity store opened" } return hubMessageFlow(context).filter(filterCondition).onEach { message -> @@ -76,7 +76,7 @@ public fun DeviceManager.storeMessagesInXodus( internal fun Flow.storeInXodus( entityStore: PersistentEntityStore, - flowFilter: (GenericMagixMessage) -> Boolean = { true }, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, ) { filter(flowFilter).onEach { message -> entityStore.encodeToEntity(message, "MagixMessage") @@ -88,7 +88,7 @@ public fun Application.storeInXodus( flow: MutableSharedFlow, meta: Meta = Meta.EMPTY, factory: Factory = defaultPersistentStoreFactory, - flowFilter: (GenericMagixMessage) -> Boolean = { true }, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, ) { val entityStore = factory(meta) diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 5ab4372..03b62f1 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -15,6 +15,7 @@ import ru.mipt.npm.controls.controllers.install import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration import ru.mipt.npm.controls.mongo.MongoClientFactory import ru.mipt.npm.controls.mongo.connectMongo +import ru.mipt.npm.controls.xodus.XODUS_STORE_PROPERTY import ru.mipt.npm.controls.xodus.storeInXodus import ru.mipt.npm.controls.xodus.storeMessagesInXodus import ru.mipt.npm.magix.api.MagixEndpoint @@ -55,9 +56,7 @@ class VirtualCarController : Controller(), ContextAware { //starting magix event loop and connect it to entity store magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> storeInXodus( flow, Meta { - "xodusConfig" put { - "entityStorePath" put VirtualCarControllerConfig.magixEntityStorePath.toString() - } + XODUS_STORE_PROPERTY put VirtualCarControllerConfig.magixEntityStorePath.toString() }) } magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index d2880ba..ffed3a2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index a1406e3..896d04d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,7 +5,7 @@ enableFeaturePreview("VERSION_CATALOGS") pluginManagement { - val toolsVersion = "0.10.7" + val toolsVersion = "0.10.5" repositories { maven("https://repo.kotlin.link") @@ -29,7 +29,7 @@ dependencyResolutionManagement { versionCatalogs { create("npm") { - from("ru.mipt.npm:version-catalog:0.10.7") + from("ru.mipt.npm:version-catalog:0.10.5") } } } From f977aca237ce281fa1da295e6c6b5b50e077c29c Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Thu, 16 Dec 2021 17:25:35 +0300 Subject: [PATCH 41/74] Add storeInMongo for magix server --- controls-mongo/build.gradle.kts | 1 + .../ru/mipt/npm/controls/mongo/connections.kt | 55 +++++++++++++++---- .../controls/demo/car/VirtualCarController.kt | 6 +- 3 files changed, 48 insertions(+), 14 deletions(-) diff --git a/controls-mongo/build.gradle.kts b/controls-mongo/build.gradle.kts index e6ce49d..624b71f 100644 --- a/controls-mongo/build.gradle.kts +++ b/controls-mongo/build.gradle.kts @@ -9,5 +9,6 @@ dependencies { implementation(projects.controlsCore) implementation(projects.magix.magixApi) implementation(projects.controlsMagixClient) + implementation(projects.magix.magixServer) implementation("org.litote.kmongo:kmongo-coroutine-serialization:$kmongoVersion") } diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt index fded873..a8a7800 100644 --- a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt +++ b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt @@ -1,20 +1,22 @@ package ru.mipt.npm.controls.mongo +import io.ktor.application.* import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.job import kotlinx.coroutines.launch import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import org.litote.kmongo.coroutine.CoroutineClient +import org.litote.kmongo.coroutine.CoroutineCollection import org.litote.kmongo.coroutine.coroutine import org.litote.kmongo.coroutine.insertOne import org.litote.kmongo.reactivestreams.KMongo import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.hubMessageFlow +import ru.mipt.npm.magix.server.GenericMagixMessage import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.context.debug @@ -22,17 +24,19 @@ import space.kscience.dataforge.context.logger import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string +import space.kscience.dataforge.names.Name -internal object DefaultMongoConfig { - const val databaseName = "deviceMessage" -} +private const val DEFAULT_MONGO_DATABASE_URL = "mongodb://mongoadmin:secret@localhost:27888" +private const val DEFAULT_DEVICE_MESSAGE_DATABASE_NAME = "deviceMessage" +private const val DEFAULT_MAGIX_MESSAGE_DATABASE_NAME = "magixMessage" +public val MONGO_DATABASE_URL_PROPERTY: Name = Name.of("mongo", "databaseUrl") +public val MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY: Name = Name.of("mongo", "deviceMessageDatabaseName") +public val MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY: Name = Name.of("mongo", "magixMessageDatabaseName") -public object MongoClientFactory : Factory { - private const val connectionString: String = "mongodb://mongoadmin:secret@localhost:27888" - - override fun invoke(meta: Meta, context: Context): CoroutineClient = meta["mongoConfig"]?.get("connectionString")?.string?.let { +public object DefaultMongoClientFactory : Factory { + override fun invoke(meta: Meta, context: Context): CoroutineClient = meta[MONGO_DATABASE_URL_PROPERTY]?.string?.let { KMongo.createClient(it).coroutine - } ?: KMongo.createClient(connectionString).coroutine + } ?: KMongo.createClient(DEFAULT_MONGO_DATABASE_URL).coroutine } @OptIn(InternalCoroutinesApi::class) @@ -43,7 +47,7 @@ public fun DeviceManager.connectMongo( val client = factory.invoke(meta, context) logger.debug { "Mongo client opened" } val collection = client - .getDatabase(meta["mongoConfig"]?.get("databaseName")?.string ?: DefaultMongoConfig.databaseName) + .getDatabase(meta[MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY]?.string ?: DEFAULT_DEVICE_MESSAGE_DATABASE_NAME) .getCollection() return hubMessageFlow(context).filter(filterCondition).onEach { message -> context.launch { @@ -56,3 +60,30 @@ public fun DeviceManager.connectMongo( } } } + +internal fun Flow.storeInMongo( + collection: CoroutineCollection, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, +) { + filter(flowFilter).onEach { message -> + collection.insertOne(Json.encodeToString(message)) + } +} + +@OptIn(InternalCoroutinesApi::class) +public fun Application.storeInMongo( + flow: MutableSharedFlow, + meta: Meta = Meta.EMPTY, + factory: Factory = DefaultMongoClientFactory, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, +) { + val client = factory.invoke(meta) + val collection = client + .getDatabase(meta[MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY]?.string ?: DEFAULT_MAGIX_MESSAGE_DATABASE_NAME) + .getCollection() + + flow.storeInMongo(collection, flowFilter) + coroutineContext.job.invokeOnCompletion(onCancelling = true) { + client.close() + } +} diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 03b62f1..0dd6ffe 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -13,8 +13,9 @@ 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.car.IVirtualCar.Companion.acceleration -import ru.mipt.npm.controls.mongo.MongoClientFactory +import ru.mipt.npm.controls.mongo.DefaultMongoClientFactory import ru.mipt.npm.controls.mongo.connectMongo +import ru.mipt.npm.controls.mongo.storeInMongo import ru.mipt.npm.controls.xodus.XODUS_STORE_PROPERTY import ru.mipt.npm.controls.xodus.storeInXodus import ru.mipt.npm.controls.xodus.storeMessagesInXodus @@ -58,12 +59,13 @@ class VirtualCarController : Controller(), ContextAware { storeInXodus( flow, Meta { XODUS_STORE_PROPERTY put VirtualCarControllerConfig.magixEntityStorePath.toString() }) + storeInMongo(flow) } magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) //connect to device entity store xodusStorageJob = deviceManager.storeMessagesInXodus() //Create mongo client and connect to MongoDB - mongoStorageJob = deviceManager.connectMongo(MongoClientFactory) + mongoStorageJob = deviceManager.connectMongo(DefaultMongoClientFactory) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) deviceManager.connectToMagix(deviceEndpoint) From fe1575c5ee9ff7254ca13d85d2b8f90d34894609 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Thu, 16 Dec 2021 18:18:47 +0300 Subject: [PATCH 42/74] Add getPropertyHistory for xodus --- .../mipt/npm/controls/xodus/util/queries.kt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt index ee9361f..5d7f959 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt @@ -1,9 +1,14 @@ package ru.mipt.npm.controls.xodus.util +import jetbrains.exodus.entitystore.PersistentEntityStore import jetbrains.exodus.entitystore.StoreTransaction import kotlinx.datetime.Instant import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.xodus.defaultPersistentStoreFactory import ru.mipt.npm.controls.xodus.toPropertyChangedMessage +import ru.mipt.npm.xodus.serialization.json.decodeFromEntity +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.meta.Meta public fun StoreTransaction.selectPropertyChangedMessagesFromRange( range: ClosedRange @@ -13,3 +18,26 @@ public fun StoreTransaction.selectPropertyChangedMessagesFromRange( range.start.toEpochMilliseconds(), range.endInclusive.toEpochMilliseconds() ).mapNotNull { it.toPropertyChangedMessage() } + +/** + * @return the list of deviceMessages that describes changes of specified property of specified device sorted by time + * @param sourceDeviceName a name of device, history of which property we want to get + * @param propertyName a name of property, history of which we want to get + * @param factory a factory that produce mongo clients + */ +public fun getPropertyHistory( + sourceDeviceName: String, + propertyName: String, + factory: Factory = defaultPersistentStoreFactory, + meta: Meta = Meta.EMPTY +): List { + return factory(meta).use { store -> + store.computeInTransaction { txn -> + txn.find("DeviceMessage", "type", "property.changed").asSequence() + .filter { it?.getProperty("sourceDevice")?.let { it == sourceDeviceName } ?: false && + it?.getProperty("property")?.let { it == propertyName } ?: false + }.sortedByDescending { it?.getProperty("time")?.let { timeStr -> Instant.parse(timeStr as String) } } + .toList().map { txn.decodeFromEntity(it) } + } + } +} From 3ebbec35fe6f609b40a3348ee1f34ba1bff58c14 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Thu, 16 Dec 2021 23:14:32 +0300 Subject: [PATCH 43/74] Add some docs --- controls-mongo/README.md | 26 +++++++++++++++++++ .../ru/mipt/npm/controls/mongo/connections.kt | 13 ++++++++++ controls-xodus/README.md | 26 +++++++++++++++++++ .../ru/mipt/npm/controls/xodus/connections.kt | 13 ++++++++++ 4 files changed, 78 insertions(+) create mode 100644 controls-mongo/README.md create mode 100644 controls-xodus/README.md diff --git a/controls-mongo/README.md b/controls-mongo/README.md new file mode 100644 index 0000000..a627574 --- /dev/null +++ b/controls-mongo/README.md @@ -0,0 +1,26 @@ +# Description +This module allows you to store [DeviceMessages](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt) +from certain [DeviceManager](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt) +or [MagixMessages](magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt) +from [magix server](/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt) +in [mongoDB](https://www.mongodb.com/). + +# Usage + +All usage examples can be found in [VirtualCarController](/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt). + +## Storage from Device Manager + +Just call storeMessagesInXodus. For more details, you can see comments in [source code](/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt) + +## Storage from Magix Server + +Just pass such lambda as parameter to startMagixServer: +```kotlin +{ flow -> + // some code + storeInMongo(flow) + // some code +} +``` +For more details, you can see comments in [source code](/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt) diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt index a8a7800..e041171 100644 --- a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt +++ b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt @@ -39,6 +39,13 @@ public object DefaultMongoClientFactory : Factory { } ?: KMongo.createClient(DEFAULT_MONGO_DATABASE_URL).coroutine } +/** + * Begin to store DeviceMessages from this DeviceManager + * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. + * DeviceManager's meta and context will be used for in invoke method. + * @param filterCondition allow you to specify messages which we want to store. Always true by default. + * @return Job which responsible for our storage + */ @OptIn(InternalCoroutinesApi::class) public fun DeviceManager.connectMongo( factory: Factory, @@ -70,6 +77,12 @@ internal fun Flow.storeInMongo( } } +/** Begin to store MagixMessages from certain flow + * @param flow flow of messages which we will store + * @param meta Meta which may have some configuration parameters for our storage and will be used in invoke method of factory + * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. + * @param flowFilter allow you to specify messages which we want to store. Always true by default. + */ @OptIn(InternalCoroutinesApi::class) public fun Application.storeInMongo( flow: MutableSharedFlow, diff --git a/controls-xodus/README.md b/controls-xodus/README.md new file mode 100644 index 0000000..dca332c --- /dev/null +++ b/controls-xodus/README.md @@ -0,0 +1,26 @@ +# Description +This module allows you to store [DeviceMessages](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt) +from certain [DeviceManager](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt) +or [MagixMessages](magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt) +from [magix server](/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt) +in [xodus database](https://github.com/JetBrains/xodus). + +# Usage + +All usage examples can be found in [VirtualCarController](/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt). + +## Storage from Device Manager + +Just call connectMongo. For more details, you can see comments in [source code](/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt) + +## Storage from Magix Server + +Just pass such lambda as parameter to startMagixServer: +```kotlin +{ flow -> + // some code + storeInXodus(flow) + // some code +} +``` +For more details, you can see comments in [source code](/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt) diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt index 8c72785..3fe3ce6 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt @@ -36,6 +36,13 @@ internal val defaultPersistentStoreFactory = object : Factory = defaultPersistentStoreFactory, @@ -83,6 +90,12 @@ internal fun Flow.storeInXodus( } } +/** Begin to store MagixMessages from certain flow + * @param flow flow of messages which we will store + * @param meta Meta which may have some configuration parameters for our storage and will be used in invoke method of factory + * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. + * @param flowFilter allow you to specify messages which we want to store. Always true by default. + */ @OptIn(InternalCoroutinesApi::class) public fun Application.storeInXodus( flow: MutableSharedFlow, From 89bb132e36df4ae92f3e05f258bd0f985fad83a3 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Fri, 14 Jan 2022 21:47:48 +0300 Subject: [PATCH 44/74] Refactor synchronous storage api --- controls-storage/build.gradle.kts | 27 +++++ .../synchronous/SynchronousStorageClient.kt | 14 +++ .../synchronous/commonApi.kt | 61 ++++++++++ .../controls/storage/synchronous/jvmApi.kt | 42 +++++++ controls-xodus/README.md | 26 ---- controls-xodus/build.gradle.kts | 6 +- .../controls/xodus/SynchronousXodusClient.kt | 66 +++++++++++ .../ru/mipt/npm/controls/xodus/connections.kt | 112 ------------------ .../ru/mipt/npm/controls/xodus/converters.kt | 73 ------------ .../mipt/npm/controls/xodus/util/queries.kt | 43 ------- .../mipt/npm/controls/xodus/ConvertersTest.kt | 60 ---------- .../npm/controls/xodus/util/QueriesTest.kt | 62 ---------- demo/car/build.gradle.kts | 1 + .../controls/demo/car/VirtualCarController.kt | 11 +- settings.gradle.kts | 4 +- .../npm/xodus/serialization/json/decoder.kt | 3 +- .../npm/xodus/serialization/json/encoder.kt | 6 + 17 files changed, 229 insertions(+), 388 deletions(-) create mode 100644 controls-storage/build.gradle.kts create mode 100644 controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt create mode 100644 controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt create mode 100644 controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/synchronous/jvmApi.kt delete mode 100644 controls-xodus/README.md create mode 100644 controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/SynchronousXodusClient.kt delete mode 100644 controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt delete mode 100644 controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt delete mode 100644 controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt delete mode 100644 controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/ConvertersTest.kt delete mode 100644 controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt diff --git a/controls-storage/build.gradle.kts b/controls-storage/build.gradle.kts new file mode 100644 index 0000000..beec52d --- /dev/null +++ b/controls-storage/build.gradle.kts @@ -0,0 +1,27 @@ +plugins { + id("ru.mipt.npm.gradle.mpp") + `maven-publish` +} + +val dataforgeVersion: String by rootProject.extra +val kotlinx_io_version = "0.1.1" + +kotlin { + sourceSets { + commonMain { + dependencies { + implementation(projects.controlsCore) + implementation("org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version") + } + } + + jvmMain { + dependencies { + implementation(projects.magix.magixApi) + implementation(projects.controlsMagixClient) + implementation(projects.magix.magixServer) + implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinx_io_version") + } + } + } +} diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt new file mode 100644 index 0000000..ef4e22f --- /dev/null +++ b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt @@ -0,0 +1,14 @@ +package ru.mipt.npm.controls.storage.synchronous + +import kotlinx.io.core.Closeable +import ru.mipt.npm.controls.api.PropertyChangedMessage +import kotlin.reflect.KClass + +public interface SynchronousStorageClient : Closeable { + public fun storeValue(value: T, storageKind: StorageKind, clazz: KClass) + + public fun getPropertyHistory(sourceDeviceName: String, propertyName: String): List +} + +public inline fun SynchronousStorageClient.storeValue(value: T, storageKind: StorageKind): Unit = + storeValue(value, storageKind, T::class) diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt new file mode 100644 index 0000000..29155dc --- /dev/null +++ b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt @@ -0,0 +1,61 @@ +package ru.mipt.npm.controls.storage.synchronous + +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.* +import kotlinx.io.core.use +import ru.mipt.npm.controls.api.DeviceMessage +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.controllers.DeviceManager +import ru.mipt.npm.controls.controllers.hubMessageFlow +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.context.debug +import space.kscience.dataforge.context.logger +import space.kscience.dataforge.meta.Meta + +public enum class StorageKind { + DEVICE_HUB, + MAGIX_SERVER +} + +/** + * Begin to store DeviceMessages from this DeviceManager + * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. + * DeviceManager's meta and context will be used for in invoke method. + * @param filterCondition allow you to specify messages which we want to store. Always true by default. + * @return Job which responsible for our storage + */ +@OptIn(InternalCoroutinesApi::class) +public fun DeviceManager.storeMessages( + factory: Factory, + filterCondition: suspend (DeviceMessage) -> Boolean = { true }, +): Job { + val client = factory(meta, context) + logger.debug { "Storage client created" } + + return hubMessageFlow(context).filter(filterCondition).onEach { message -> + client.storeValue(message, StorageKind.DEVICE_HUB) + }.launchIn(context).apply { + invokeOnCompletion(onCancelling = true) { + client.close() + logger.debug { "Storage client closed" } + } + } +} + +/** + * @return the list of deviceMessages that describes changes of specified property of specified device sorted by time + * @param sourceDeviceName a name of device, history of which property we want to get + * @param propertyName a name of property, history of which we want to get + * @param factory a factory that produce mongo clients + */ +public fun getPropertyHistory( + sourceDeviceName: String, + propertyName: String, + factory: Factory, + meta: Meta = Meta.EMPTY +): List { + return factory(meta).use { + it.getPropertyHistory(sourceDeviceName, propertyName) + } +} diff --git a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/synchronous/jvmApi.kt b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/synchronous/jvmApi.kt new file mode 100644 index 0000000..6c31500 --- /dev/null +++ b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/synchronous/jvmApi.kt @@ -0,0 +1,42 @@ +package ru.mipt.npm.controls.storage.synchronous + +import io.ktor.application.* +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.job +import ru.mipt.npm.magix.server.GenericMagixMessage +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.meta.Meta + +internal fun Flow.store( + client: SynchronousStorageClient, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, +) { + filter(flowFilter).onEach { message -> + client.storeValue(message, StorageKind.MAGIX_SERVER) + } +} + +/** Begin to store MagixMessages from certain flow + * @param flow flow of messages which we will store + * @param meta Meta which may have some configuration parameters for our storage and will be used in invoke method of factory + * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. + * @param flowFilter allow you to specify messages which we want to store. Always true by default. + */ +@OptIn(InternalCoroutinesApi::class) +public fun Application.store( + flow: MutableSharedFlow, + meta: Meta = Meta.EMPTY, + factory: Factory, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, +) { + val client = factory(meta) + + flow.store(client, flowFilter) + coroutineContext.job.invokeOnCompletion(onCancelling = true) { + client.close() + } +} diff --git a/controls-xodus/README.md b/controls-xodus/README.md deleted file mode 100644 index dca332c..0000000 --- a/controls-xodus/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Description -This module allows you to store [DeviceMessages](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt) -from certain [DeviceManager](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt) -or [MagixMessages](magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt) -from [magix server](/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt) -in [xodus database](https://github.com/JetBrains/xodus). - -# Usage - -All usage examples can be found in [VirtualCarController](/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt). - -## Storage from Device Manager - -Just call connectMongo. For more details, you can see comments in [source code](/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt) - -## Storage from Magix Server - -Just pass such lambda as parameter to startMagixServer: -```kotlin -{ flow -> - // some code - storeInXodus(flow) - // some code -} -``` -For more details, you can see comments in [source code](/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt) diff --git a/controls-xodus/build.gradle.kts b/controls-xodus/build.gradle.kts index 9421f36..b07ed6b 100644 --- a/controls-xodus/build.gradle.kts +++ b/controls-xodus/build.gradle.kts @@ -6,11 +6,9 @@ plugins { val xodusVersion = "1.3.232" dependencies { - implementation(projects.controlsCore) - implementation(projects.magix.magixApi) - implementation(projects.controlsMagixClient) - implementation(projects.magix.magixServer) implementation(projects.xodusSerialization) + implementation(projects.controlsStorage) + implementation(projects.controlsCore) implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") implementation("org.jetbrains.xodus:xodus-environment:$xodusVersion") implementation("org.jetbrains.xodus:xodus-vfs:$xodusVersion") diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/SynchronousXodusClient.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/SynchronousXodusClient.kt new file mode 100644 index 0000000..344d34e --- /dev/null +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/SynchronousXodusClient.kt @@ -0,0 +1,66 @@ +package ru.mipt.npm.controls.xodus + +import jetbrains.exodus.entitystore.PersistentEntityStore +import jetbrains.exodus.entitystore.PersistentEntityStores +import kotlinx.datetime.Instant +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.storage.synchronous.StorageKind +import ru.mipt.npm.controls.storage.synchronous.SynchronousStorageClient +import ru.mipt.npm.xodus.serialization.json.decodeFromEntity +import ru.mipt.npm.xodus.serialization.json.encodeToEntity +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.string +import space.kscience.dataforge.names.Name +import kotlin.reflect.KClass + +private const val DEFAULT_XODUS_STORE_PATH = ".storage" +public val XODUS_STORE_PROPERTY: Name = Name.of("xodus", "entityStorePath") + +private const val DEVICE_HUB_ENTITY_TYPE = "DeviceMessage" +private const val MAGIX_SERVER_ENTITY_TYPE = "MagixMessage" + +internal class SynchronousXodusClient(private val entityStore: PersistentEntityStore) : SynchronousStorageClient { + override fun storeValue(value: T, storageKind: StorageKind, clazz: KClass) { + val entityType = when (storageKind) { + StorageKind.DEVICE_HUB -> DEVICE_HUB_ENTITY_TYPE + StorageKind.MAGIX_SERVER -> MAGIX_SERVER_ENTITY_TYPE + } + + entityStore.encodeToEntity(value, entityType, clazz) + } + + override fun getPropertyHistory( + sourceDeviceName: String, + propertyName: String + ): List { + return entityStore.computeInTransaction { txn -> + txn.find(DEVICE_HUB_ENTITY_TYPE, "type", "property.changed").asSequence() + .filter { it?.getProperty("sourceDevice")?.let { it == sourceDeviceName } ?: false && + it?.getProperty("property")?.let { it == propertyName } ?: false + }.sortedByDescending { it?.getProperty("time")?.let { timeStr -> Instant.parse(timeStr as String) } } + .toList().map { txn.decodeFromEntity(it) } + } + } + + override fun close() { + entityStore.close() + } +} + +private fun Context.getPersistentEntityStore(meta: Meta = Meta.EMPTY): PersistentEntityStore { + val storePath = meta[XODUS_STORE_PROPERTY]?.string + ?: properties[XODUS_STORE_PROPERTY]?.string + ?: DEFAULT_XODUS_STORE_PATH + + return PersistentEntityStores.newInstance(storePath) +} + +public object DefaultSynchronousXodusClientFactory : Factory { + override fun invoke(meta: Meta, context: Context): SynchronousStorageClient { + val entityStore = context.getPersistentEntityStore(meta) + return SynchronousXodusClient(entityStore) + } +} diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt deleted file mode 100644 index 3fe3ce6..0000000 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt +++ /dev/null @@ -1,112 +0,0 @@ -package ru.mipt.npm.controls.xodus - -import io.ktor.application.Application -import jetbrains.exodus.entitystore.PersistentEntityStore -import jetbrains.exodus.entitystore.PersistentEntityStores -import kotlinx.coroutines.InternalCoroutinesApi -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.job -import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.controllers.DeviceManager -import ru.mipt.npm.controls.controllers.hubMessageFlow -import ru.mipt.npm.magix.server.GenericMagixMessage -import ru.mipt.npm.xodus.serialization.json.encodeToEntity -import space.kscience.dataforge.context.Context -import space.kscience.dataforge.context.Factory -import space.kscience.dataforge.context.debug -import space.kscience.dataforge.context.logger -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.string -import space.kscience.dataforge.names.Name - -private const val DEFAULT_XODUS_STORE_PATH = ".storage" -public val XODUS_STORE_PROPERTY: Name = Name.of("xodus", "entityStorePath") - -private fun Context.getPersistentEntityStore(meta: Meta = Meta.EMPTY): PersistentEntityStore { - val storePath = meta[XODUS_STORE_PROPERTY]?.string - ?: properties[XODUS_STORE_PROPERTY]?.string - ?: DEFAULT_XODUS_STORE_PATH - - return PersistentEntityStores.newInstance(storePath) -} - -internal val defaultPersistentStoreFactory = object : Factory { - override fun invoke(meta: Meta, context: Context): PersistentEntityStore = context.getPersistentEntityStore(meta) -} - -/** - * Begin to store DeviceMessages from this DeviceManager - * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. - * DeviceManager's meta and context will be used for in invoke method. - * @param filterCondition allow you to specify messages which we want to store. Always true by default. - * @return Job which responsible for our storage - */ -@OptIn(InternalCoroutinesApi::class) -public fun DeviceManager.storeMessagesInXodus( - factory: Factory = defaultPersistentStoreFactory, - filterCondition: suspend (DeviceMessage) -> Boolean = { true }, -): Job { - val entityStore = factory(meta, context) - logger.debug { "Device entity store opened" } - - return hubMessageFlow(context).filter(filterCondition).onEach { message -> - entityStore.encodeToEntity(message, "DeviceMessage") - }.launchIn(context).apply { - invokeOnCompletion(onCancelling = true) { - entityStore.close() - logger.debug { "Device entity store closed" } - } - } -} - -//public fun CoroutineScope.startMagixServer( -// entityStore: PersistentEntityStore, -// flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, -// port: Int = MagixEndpoint.DEFAULT_MAGIX_HTTP_PORT, -// buffer: Int = 100, -// enableRawRSocket: Boolean = true, -// enableZmq: Boolean = true, -// applicationConfiguration: Application.(MutableSharedFlow) -> Unit = {}, -//): ApplicationEngine = startMagixServer( -// port, buffer, enableRawRSocket, enableZmq -//) { flow -> -// applicationConfiguration(flow) -// flow.filter(flowFilter).onEach { message -> -// entityStore.executeInTransaction { txn -> -// val entity = txn.newEntity("MagixMessage") -// entity.setProperty("value", message.toString()) -// } -// } -//} - -internal fun Flow.storeInXodus( - entityStore: PersistentEntityStore, - flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, -) { - filter(flowFilter).onEach { message -> - entityStore.encodeToEntity(message, "MagixMessage") - } -} - -/** Begin to store MagixMessages from certain flow - * @param flow flow of messages which we will store - * @param meta Meta which may have some configuration parameters for our storage and will be used in invoke method of factory - * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. - * @param flowFilter allow you to specify messages which we want to store. Always true by default. - */ -@OptIn(InternalCoroutinesApi::class) -public fun Application.storeInXodus( - flow: MutableSharedFlow, - meta: Meta = Meta.EMPTY, - factory: Factory = defaultPersistentStoreFactory, - flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, -) { - val entityStore = factory(meta) - - flow.storeInXodus(entityStore, flowFilter) - coroutineContext.job.invokeOnCompletion(onCancelling = true) { - entityStore.close() - } -} diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt deleted file mode 100644 index f7bbd87..0000000 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/converters.kt +++ /dev/null @@ -1,73 +0,0 @@ -package ru.mipt.npm.controls.xodus - -import jetbrains.exodus.entitystore.Entity -import jetbrains.exodus.entitystore.StoreTransaction -import kotlinx.datetime.Instant -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonElement -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.magix.api.MagixMessage -import space.kscience.dataforge.meta.MetaSerializer -import space.kscience.dataforge.meta.isLeaf -import space.kscience.dataforge.names.Name -import space.kscience.dataforge.values.Value -import space.kscience.dataforge.values.ValueType - -internal fun PropertyChangedMessage.toEntity(transaction: StoreTransaction): Entity { - val entity = transaction.newEntity("PropertyChangedMessage") - entity.setProperty("property", property) - entity.setProperty("value", value.toString()) - entity.setProperty("sourceDevice", sourceDevice.toString()) - targetDevice?.let { entity.setProperty("targetDevice", it.toString()) } - comment?.let { entity.setProperty("comment", it) } - time?.let { entity.setProperty("time", it.toEpochMilliseconds()) } - return entity -} - -internal fun Entity.toPropertyChangedMessage(): PropertyChangedMessage? { - if (getProperty("property") == null || getProperty("value") == null || getProperty("sourceDevice") == null) { - return null - } - - return PropertyChangedMessage( - getProperty("property") as String, - Json.decodeFromString(MetaSerializer, getProperty("value") as String), - Name.parse(getProperty("sourceDevice") as String), - getProperty("targetDevice")?.let { Name.parse(it as String) }, - getProperty("comment")?.let { it as String }, - getProperty("time")?.let { Instant.fromEpochMilliseconds(it as Long) } - ) -} - -internal fun MagixMessage.toEntity(transaction: StoreTransaction): Entity { - val entity = transaction.newEntity("MagixMessage") - entity.setProperty("format", format) - entity.setProperty("origin", origin) - if (payload is PropertyChangedMessage) { - val payloadEntity = (payload as PropertyChangedMessage).toEntity(transaction) - entity.setLink("payload", payloadEntity) - } - target?.let { entity.setProperty("target", it) } - id?.let { entity.setProperty("id", it) } - parentId?.let { entity.setProperty("parentId", it) } - user?.let { entity.setProperty("user", it.toString()) } - return entity -} - -internal fun Entity.toMagixMessage(): MagixMessage? { - if (getProperty("format") == null || getProperty("origin") == null) { - return null - } - - return getLink("payload")?.toPropertyChangedMessage()?.let { propertyChangedMessage -> - MagixMessage( - getProperty("format") as String, - getProperty("origin") as String, - propertyChangedMessage, - getProperty("target")?.let { it as String }, - getProperty("id")?.let { it as String }, - getProperty("parentId")?.let { it as String }, - getProperty("user")?.let { Json.decodeFromString(JsonElement.serializer(), it as String) } - ) - } -} diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt deleted file mode 100644 index 5d7f959..0000000 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/util/queries.kt +++ /dev/null @@ -1,43 +0,0 @@ -package ru.mipt.npm.controls.xodus.util - -import jetbrains.exodus.entitystore.PersistentEntityStore -import jetbrains.exodus.entitystore.StoreTransaction -import kotlinx.datetime.Instant -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.xodus.defaultPersistentStoreFactory -import ru.mipt.npm.controls.xodus.toPropertyChangedMessage -import ru.mipt.npm.xodus.serialization.json.decodeFromEntity -import space.kscience.dataforge.context.Factory -import space.kscience.dataforge.meta.Meta - -public fun StoreTransaction.selectPropertyChangedMessagesFromRange( - range: ClosedRange -): List = find( - "PropertyChangedMessage", - "time", - range.start.toEpochMilliseconds(), - range.endInclusive.toEpochMilliseconds() -).mapNotNull { it.toPropertyChangedMessage() } - -/** - * @return the list of deviceMessages that describes changes of specified property of specified device sorted by time - * @param sourceDeviceName a name of device, history of which property we want to get - * @param propertyName a name of property, history of which we want to get - * @param factory a factory that produce mongo clients - */ -public fun getPropertyHistory( - sourceDeviceName: String, - propertyName: String, - factory: Factory = defaultPersistentStoreFactory, - meta: Meta = Meta.EMPTY -): List { - return factory(meta).use { store -> - store.computeInTransaction { txn -> - txn.find("DeviceMessage", "type", "property.changed").asSequence() - .filter { it?.getProperty("sourceDevice")?.let { it == sourceDeviceName } ?: false && - it?.getProperty("property")?.let { it == propertyName } ?: false - }.sortedByDescending { it?.getProperty("time")?.let { timeStr -> Instant.parse(timeStr as String) } } - .toList().map { txn.decodeFromEntity(it) } - } - } -} diff --git a/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/ConvertersTest.kt b/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/ConvertersTest.kt deleted file mode 100644 index da7f4d6..0000000 --- a/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/ConvertersTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -package ru.mipt.npm.controls.xodus - -import jetbrains.exodus.entitystore.PersistentEntityStores -import kotlinx.datetime.Instant -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.magix.api.MagixMessage -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.names.Name -import java.io.File -import kotlin.test.assertEquals - -internal class ConvertersTest { - companion object { - private val storeName = ".converters_test" - private val entityStore = PersistentEntityStores.newInstance(storeName) - private val expectedMessage = MagixMessage( - "dataforge", - "dataforge", - PropertyChangedMessage( - "acceleration", - Meta { - "x" put 3.0 - "y" put 9.0 - }, - Name.parse("virtual-car"), - Name.parse("magix-virtual-car"), - time = Instant.fromEpochMilliseconds(1337) - ), - "magix-virtual-car", - user = JsonObject(content = mapOf(Pair("name", JsonPrimitive("SCADA")))) - ) - - @BeforeAll - @JvmStatic - fun createEntities() { - entityStore.executeInTransaction { - expectedMessage.toEntity(it) - } - } - - @AfterAll - @JvmStatic - fun deleteDatabase() { - entityStore.close() - File(storeName).deleteRecursively() - } - } - - @Test - fun testMagixMessageAndPropertyChangedMessageConverters() { - assertEquals(expectedMessage, entityStore.computeInReadonlyTransaction { - it.getAll("MagixMessage").first?.toMagixMessage() - }!!) - } -} diff --git a/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt b/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt deleted file mode 100644 index b09ae2e..0000000 --- a/controls-xodus/src/test/kotlin/ru/mipt/npm/controls/xodus/util/QueriesTest.kt +++ /dev/null @@ -1,62 +0,0 @@ -package ru.mipt.npm.controls.xodus.util - -import jetbrains.exodus.entitystore.PersistentEntityStores -import kotlinx.datetime.Instant -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.BeforeAll -import org.junit.jupiter.api.Test -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.xodus.toEntity -import space.kscience.dataforge.meta.Meta -import java.io.File - -internal class QueriesTest { - companion object { - private val storeName = ".queries_test" - private val entityStore = PersistentEntityStores.newInstance(storeName) - - private val propertyChangedMessages = listOf( - PropertyChangedMessage( - "", - Meta.EMPTY, - time = Instant.fromEpochMilliseconds(1000) - ), - PropertyChangedMessage( - "", - Meta.EMPTY, - time = Instant.fromEpochMilliseconds(1500) - ), - PropertyChangedMessage( - "", - Meta.EMPTY, - time = Instant.fromEpochMilliseconds(2000) - ) - ) - - @BeforeAll - @JvmStatic - fun createEntities() { - entityStore.executeInTransaction { transaction -> - propertyChangedMessages.forEach { - it.toEntity(transaction) - } - } - } - - @AfterAll - @JvmStatic - fun deleteDatabase() { - entityStore.close() - File(storeName).deleteRecursively() - } - } - - @Test - fun testFromTo() { - assertEquals(propertyChangedMessages.subList(0, 2).toSet(), entityStore.computeInReadonlyTransaction { - it.selectPropertyChangedMessagesFromRange( Instant.fromEpochMilliseconds(1000)..Instant.fromEpochMilliseconds(1500)) - }.toSet()) - } - -} \ No newline at end of file diff --git a/demo/car/build.gradle.kts b/demo/car/build.gradle.kts index ad3dda1..93968e7 100644 --- a/demo/car/build.gradle.kts +++ b/demo/car/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation(projects.controlsMagixClient) implementation(projects.controlsXodus) implementation(projects.controlsMongo) + implementation(projects.controlsStorage) implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1") diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 0dd6ffe..3796bc7 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -16,9 +16,10 @@ import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration import ru.mipt.npm.controls.mongo.DefaultMongoClientFactory import ru.mipt.npm.controls.mongo.connectMongo import ru.mipt.npm.controls.mongo.storeInMongo +import ru.mipt.npm.controls.storage.synchronous.store +import ru.mipt.npm.controls.storage.synchronous.storeMessages +import ru.mipt.npm.controls.xodus.DefaultSynchronousXodusClientFactory import ru.mipt.npm.controls.xodus.XODUS_STORE_PROPERTY -import ru.mipt.npm.controls.xodus.storeInXodus -import ru.mipt.npm.controls.xodus.storeMessagesInXodus import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.rsocket.rSocketWithTcp import ru.mipt.npm.magix.server.startMagixServer @@ -56,14 +57,14 @@ class VirtualCarController : Controller(), ContextAware { //starting magix event loop and connect it to entity store magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> - storeInXodus( flow, Meta { + store( flow, Meta { XODUS_STORE_PROPERTY put VirtualCarControllerConfig.magixEntityStorePath.toString() - }) + }, DefaultSynchronousXodusClientFactory) storeInMongo(flow) } magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) //connect to device entity store - xodusStorageJob = deviceManager.storeMessagesInXodus() + xodusStorageJob = deviceManager.storeMessages(DefaultSynchronousXodusClientFactory) //Create mongo client and connect to MongoDB mongoStorageJob = deviceManager.connectMongo(DefaultMongoClientFactory) //Launch device client and connect it to the server diff --git a/settings.gradle.kts b/settings.gradle.kts index 896d04d..827b689 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -53,6 +53,6 @@ include( ":motors", ":controls-xodus", ":controls-mongo", - ":xodus-serialization" + ":xodus-serialization", + ":controls-storage" ) -include("xodus-serialization") diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt index ff29904..eeba26b 100644 --- a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt +++ b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt @@ -38,7 +38,8 @@ internal fun StoreTransaction.decodeFromEntity(entity: Entity): JsonElement = bu public fun StoreTransaction.decodeFromEntity(entity: Entity, deserializer: DeserializationStrategy): T { val jsonElement = decodeFromEntity(entity) - return Json.decodeFromJsonElement(deserializer, jsonElement) + val json = Json { ignoreUnknownKeys = true } + return json.decodeFromJsonElement(deserializer, jsonElement) } public inline fun StoreTransaction.decodeFromEntity(entity: Entity): T = decodeFromEntity(entity, serializer()) diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt index 3cd5b79..6a160da 100644 --- a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt +++ b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt @@ -4,9 +4,11 @@ import jetbrains.exodus.entitystore.Entity import jetbrains.exodus.entitystore.EntityId import jetbrains.exodus.entitystore.PersistentEntityStore import jetbrains.exodus.entitystore.StoreTransaction +import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.json.* import kotlinx.serialization.serializer +import kotlin.reflect.KClass internal fun StoreTransaction.encodeToEntity(jsonElement: JsonElement, entity: Entity) { when (jsonElement) { @@ -69,3 +71,7 @@ public fun PersistentEntityStore.encodeToEntity(serializer: SerializationStr public inline fun PersistentEntityStore.encodeToEntity(value: T, entityType: String): EntityId = encodeToEntity(serializer(), value, entityType) + +@OptIn(InternalSerializationApi::class) +public fun PersistentEntityStore.encodeToEntity(value: T, entityType: String, clazz: KClass): EntityId = encodeToEntity( + clazz.serializer(), value, entityType) From cbdd6a477bd04dca887d1590ee7e77006ea42f75 Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Sat, 15 Jan 2022 15:42:26 +0300 Subject: [PATCH 45/74] Refactor asynchronous storage api --- controls-mongo/build.gradle.kts | 1 + .../controls/mongo/AsynchronousMongoClient.kt | 78 ++++++++++++++ .../ru/mipt/npm/controls/mongo/connections.kt | 102 ------------------ .../asynchronous/AsynchronousStorageClient.kt | 16 +++ .../asynchronous/commonApi.kt | 51 +++++++++ .../controls/storage/asynchronous/jvmApi.kt | 41 +++++++ .../controls/demo/car/VirtualCarController.kt | 10 +- 7 files changed, 192 insertions(+), 107 deletions(-) create mode 100644 controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/AsynchronousMongoClient.kt delete mode 100644 controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt create mode 100644 controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt create mode 100644 controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt create mode 100644 controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/asynchronous/jvmApi.kt diff --git a/controls-mongo/build.gradle.kts b/controls-mongo/build.gradle.kts index 624b71f..d5c81f0 100644 --- a/controls-mongo/build.gradle.kts +++ b/controls-mongo/build.gradle.kts @@ -10,5 +10,6 @@ dependencies { implementation(projects.magix.magixApi) implementation(projects.controlsMagixClient) implementation(projects.magix.magixServer) + implementation(projects.controlsStorage) implementation("org.litote.kmongo:kmongo-coroutine-serialization:$kmongoVersion") } diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/AsynchronousMongoClient.kt b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/AsynchronousMongoClient.kt new file mode 100644 index 0000000..eb4eb1d --- /dev/null +++ b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/AsynchronousMongoClient.kt @@ -0,0 +1,78 @@ +package ru.mipt.npm.controls.mongo + +import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.json.Json +import kotlinx.serialization.serializer +import org.litote.kmongo.coroutine.CoroutineClient +import org.litote.kmongo.coroutine.coroutine +import org.litote.kmongo.coroutine.insertOne +import org.litote.kmongo.reactivestreams.KMongo +import ru.mipt.npm.controls.api.DeviceMessage +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.storage.asynchronous.AsynchronousStorageClient +import ru.mipt.npm.controls.storage.synchronous.StorageKind +import ru.mipt.npm.magix.server.GenericMagixMessage +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.string +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.names.Name +import kotlin.reflect.KClass + +private const val DEFAULT_DEVICE_MESSAGE_DATABASE_NAME: String = "deviceMessage" +private const val DEFAULT_MAGIX_MESSAGE_DATABASE_NAME = "magixMessage" +private const val DEFAULT_MONGO_DATABASE_URL = "mongodb://mongoadmin:secret@localhost:27888" +public val MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY: Name = Name.of("mongo", "deviceMessageDatabaseName") +public val MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY: Name = Name.of("mongo", "magixMessageDatabaseName") +public val MONGO_DATABASE_URL_PROPERTY: Name = Name.of("mongo", "databaseUrl") + +internal class AsynchronousMongoClient( + private val client: CoroutineClient, + private val meta: Meta = Meta.EMPTY +) : AsynchronousStorageClient { + @OptIn(InternalSerializationApi::class) + override suspend fun storeValue(value: T, storageKind: StorageKind, clazz: KClass) { + when (storageKind) { + StorageKind.DEVICE_HUB -> { + val collection = client + .getDatabase( + meta[MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY]?.string + ?: DEFAULT_DEVICE_MESSAGE_DATABASE_NAME + ) + .getCollection() + + collection.insertOne(Json.encodeToString(clazz.serializer(), value)) + } + + StorageKind.MAGIX_SERVER -> { + val collection = client + .getDatabase(meta[MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY]?.string ?: DEFAULT_MAGIX_MESSAGE_DATABASE_NAME) + .getCollection() + + collection.insertOne(Json.encodeToString(clazz.serializer(), value)) + } + } + } + + override suspend fun getPropertyHistory( + sourceDeviceName: String, + propertyName: String + ): List { + TODO("Not yet implemented: problems with deserialization") + } + + override fun close() { + client.close() + } +} + +public object DefaultAsynchronousMongoClientFactory : Factory { + override fun invoke(meta: Meta, context: Context): AsynchronousStorageClient { + val client = meta[MONGO_DATABASE_URL_PROPERTY]?.string?.let { + KMongo.createClient(it).coroutine + } ?: KMongo.createClient(DEFAULT_MONGO_DATABASE_URL).coroutine + + return AsynchronousMongoClient(client, meta) + } +} diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt deleted file mode 100644 index e041171..0000000 --- a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt +++ /dev/null @@ -1,102 +0,0 @@ -package ru.mipt.npm.controls.mongo - -import io.ktor.application.* -import kotlinx.coroutines.InternalCoroutinesApi -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.job -import kotlinx.coroutines.launch -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json -import org.litote.kmongo.coroutine.CoroutineClient -import org.litote.kmongo.coroutine.CoroutineCollection -import org.litote.kmongo.coroutine.coroutine -import org.litote.kmongo.coroutine.insertOne -import org.litote.kmongo.reactivestreams.KMongo -import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.controllers.DeviceManager -import ru.mipt.npm.controls.controllers.hubMessageFlow -import ru.mipt.npm.magix.server.GenericMagixMessage -import space.kscience.dataforge.context.Context -import space.kscience.dataforge.context.Factory -import space.kscience.dataforge.context.debug -import space.kscience.dataforge.context.logger -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.string -import space.kscience.dataforge.names.Name - -private const val DEFAULT_MONGO_DATABASE_URL = "mongodb://mongoadmin:secret@localhost:27888" -private const val DEFAULT_DEVICE_MESSAGE_DATABASE_NAME = "deviceMessage" -private const val DEFAULT_MAGIX_MESSAGE_DATABASE_NAME = "magixMessage" -public val MONGO_DATABASE_URL_PROPERTY: Name = Name.of("mongo", "databaseUrl") -public val MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY: Name = Name.of("mongo", "deviceMessageDatabaseName") -public val MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY: Name = Name.of("mongo", "magixMessageDatabaseName") - -public object DefaultMongoClientFactory : Factory { - override fun invoke(meta: Meta, context: Context): CoroutineClient = meta[MONGO_DATABASE_URL_PROPERTY]?.string?.let { - KMongo.createClient(it).coroutine - } ?: KMongo.createClient(DEFAULT_MONGO_DATABASE_URL).coroutine -} - -/** - * Begin to store DeviceMessages from this DeviceManager - * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. - * DeviceManager's meta and context will be used for in invoke method. - * @param filterCondition allow you to specify messages which we want to store. Always true by default. - * @return Job which responsible for our storage - */ -@OptIn(InternalCoroutinesApi::class) -public fun DeviceManager.connectMongo( - factory: Factory, - filterCondition: suspend (DeviceMessage) -> Boolean = { true } -): Job { - val client = factory.invoke(meta, context) - logger.debug { "Mongo client opened" } - val collection = client - .getDatabase(meta[MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY]?.string ?: DEFAULT_DEVICE_MESSAGE_DATABASE_NAME) - .getCollection() - return hubMessageFlow(context).filter(filterCondition).onEach { message -> - context.launch { - collection.insertOne(Json.encodeToString(message)) - } - }.launchIn(context).apply { - invokeOnCompletion(onCancelling = true) { - logger.debug { "Mongo client closed" } - client.close() - } - } -} - -internal fun Flow.storeInMongo( - collection: CoroutineCollection, - flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, -) { - filter(flowFilter).onEach { message -> - collection.insertOne(Json.encodeToString(message)) - } -} - -/** Begin to store MagixMessages from certain flow - * @param flow flow of messages which we will store - * @param meta Meta which may have some configuration parameters for our storage and will be used in invoke method of factory - * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. - * @param flowFilter allow you to specify messages which we want to store. Always true by default. - */ -@OptIn(InternalCoroutinesApi::class) -public fun Application.storeInMongo( - flow: MutableSharedFlow, - meta: Meta = Meta.EMPTY, - factory: Factory = DefaultMongoClientFactory, - flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, -) { - val client = factory.invoke(meta) - val collection = client - .getDatabase(meta[MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY]?.string ?: DEFAULT_MAGIX_MESSAGE_DATABASE_NAME) - .getCollection() - - flow.storeInMongo(collection, flowFilter) - coroutineContext.job.invokeOnCompletion(onCancelling = true) { - client.close() - } -} diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt new file mode 100644 index 0000000..b14f1f8 --- /dev/null +++ b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt @@ -0,0 +1,16 @@ +package ru.mipt.npm.controls.storage.asynchronous + +import kotlinx.io.core.Closeable +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.storage.synchronous.StorageKind +import ru.mipt.npm.controls.storage.synchronous.SynchronousStorageClient +import kotlin.reflect.KClass + +public interface AsynchronousStorageClient : Closeable { + public suspend fun storeValue(value: T, storageKind: StorageKind, clazz: KClass) + + public suspend fun getPropertyHistory(sourceDeviceName: String, propertyName: String): List +} + +public suspend inline fun AsynchronousStorageClient.storeValue(value: T, storageKind: StorageKind): Unit = + storeValue(value, storageKind, T::class) diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt new file mode 100644 index 0000000..3877daf --- /dev/null +++ b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt @@ -0,0 +1,51 @@ +package ru.mipt.npm.controls.storage.asynchronous + +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.io.core.use +import ru.mipt.npm.controls.api.DeviceMessage +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.controllers.DeviceManager +import ru.mipt.npm.controls.controllers.hubMessageFlow +import ru.mipt.npm.controls.storage.synchronous.StorageKind +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.context.debug +import space.kscience.dataforge.context.logger +import space.kscience.dataforge.meta.Meta + +/** + * Asynchronous version of synchronous API, so for more details check relative docs + */ + +@OptIn(InternalCoroutinesApi::class) +public fun DeviceManager.storeMessages( + factory: Factory, + filterCondition: suspend (DeviceMessage) -> Boolean = { true }, +): Job { + val client = factory(meta, context) + logger.debug { "Storage client created" } + + return hubMessageFlow(context).filter(filterCondition).onEach { message -> + client.storeValue(message, StorageKind.DEVICE_HUB) + }.launchIn(context).apply { + invokeOnCompletion(onCancelling = true) { + client.close() + logger.debug { "Storage client closed" } + } + } +} + +public suspend fun getPropertyHistory( + sourceDeviceName: String, + propertyName: String, + factory: Factory, + meta: Meta = Meta.EMPTY +): List { + return factory(meta).use { + it.getPropertyHistory(sourceDeviceName, propertyName) + } +} + diff --git a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/asynchronous/jvmApi.kt b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/asynchronous/jvmApi.kt new file mode 100644 index 0000000..61c634c --- /dev/null +++ b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/asynchronous/jvmApi.kt @@ -0,0 +1,41 @@ +package ru.mipt.npm.controls.storage.asynchronous + +import io.ktor.application.* +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.job +import ru.mipt.npm.controls.storage.synchronous.StorageKind +import ru.mipt.npm.magix.server.GenericMagixMessage +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.meta.Meta + +/** + * Asynchronous version of synchronous API, so for more details check relative docs + */ + +internal fun Flow.store( + client: AsynchronousStorageClient, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, +) { + filter(flowFilter).onEach { message -> + client.storeValue(message, StorageKind.MAGIX_SERVER) + } +} + +@OptIn(InternalCoroutinesApi::class) +public fun Application.store( + flow: MutableSharedFlow, + factory: Factory, + meta: Meta = Meta.EMPTY, + flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, +) { + val client = factory(meta) + + flow.store(client, flowFilter) + coroutineContext.job.invokeOnCompletion(onCancelling = true) { + client.close() + } +} diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 3796bc7..d13c8ef 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -13,9 +13,9 @@ 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.car.IVirtualCar.Companion.acceleration -import ru.mipt.npm.controls.mongo.DefaultMongoClientFactory -import ru.mipt.npm.controls.mongo.connectMongo -import ru.mipt.npm.controls.mongo.storeInMongo +import ru.mipt.npm.controls.mongo.DefaultAsynchronousMongoClientFactory +import ru.mipt.npm.controls.storage.asynchronous.store +import ru.mipt.npm.controls.storage.asynchronous.storeMessages import ru.mipt.npm.controls.storage.synchronous.store import ru.mipt.npm.controls.storage.synchronous.storeMessages import ru.mipt.npm.controls.xodus.DefaultSynchronousXodusClientFactory @@ -60,13 +60,13 @@ class VirtualCarController : Controller(), ContextAware { store( flow, Meta { XODUS_STORE_PROPERTY put VirtualCarControllerConfig.magixEntityStorePath.toString() }, DefaultSynchronousXodusClientFactory) - storeInMongo(flow) + store(flow, DefaultAsynchronousMongoClientFactory) } magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) //connect to device entity store xodusStorageJob = deviceManager.storeMessages(DefaultSynchronousXodusClientFactory) //Create mongo client and connect to MongoDB - mongoStorageJob = deviceManager.connectMongo(DefaultMongoClientFactory) + mongoStorageJob = deviceManager.storeMessages(DefaultAsynchronousMongoClientFactory) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) deviceManager.connectToMagix(deviceEndpoint) From 21c13ce3afafbee6fee9ea1ba779725628074d5a Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Sat, 15 Jan 2022 16:00:23 +0300 Subject: [PATCH 46/74] Update propertyHistoryTest for xodus --- .../src/test/kotlin/PropertyHistoryTest.kt | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 controls-xodus/src/test/kotlin/PropertyHistoryTest.kt diff --git a/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt b/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt new file mode 100644 index 0000000..4df90ba --- /dev/null +++ b/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt @@ -0,0 +1,66 @@ +import jetbrains.exodus.entitystore.PersistentEntityStores +import kotlinx.datetime.Instant +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import ru.mipt.npm.controls.api.DeviceMessage +import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.storage.synchronous.getPropertyHistory +import ru.mipt.npm.controls.xodus.DefaultSynchronousXodusClientFactory +import ru.mipt.npm.controls.xodus.XODUS_STORE_PROPERTY +import ru.mipt.npm.xodus.serialization.json.encodeToEntity +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.names.Name +import java.io.File + +internal class PropertyHistoryTest { + companion object { + private val storeName = ".property_history_test" + private val entityStore = PersistentEntityStores.newInstance(storeName) + + private val propertyChangedMessages = listOf( + PropertyChangedMessage( + "speed", + Meta.EMPTY, + time = Instant.fromEpochMilliseconds(1000), + sourceDevice = Name.of("virtual-car") + ), + PropertyChangedMessage( + "acceleration", + Meta.EMPTY, + time = Instant.fromEpochMilliseconds(1500), + sourceDevice = Name.of("virtual-car") + ), + PropertyChangedMessage( + "speed", + Meta.EMPTY, + time = Instant.fromEpochMilliseconds(2000), + sourceDevice = Name.of("magix-virtual-car") + ) + ) + + @BeforeAll + @JvmStatic + fun createEntities() { + propertyChangedMessages.forEach { + entityStore.encodeToEntity(it, "DeviceMessage") + } + entityStore.close() + } + + @AfterAll + @JvmStatic + fun deleteDatabase() { + File(storeName).deleteRecursively() + } + } + + @Test + fun getPropertyHistoryTest() { + assertEquals(listOf(propertyChangedMessages[0]), getPropertyHistory( + "virtual-car", "speed", DefaultSynchronousXodusClientFactory, Meta { + XODUS_STORE_PROPERTY put storeName + })) + } +} \ No newline at end of file From 0c4c2e4cc0a789199b508d5feb04714c64c4779b Mon Sep 17 00:00:00 2001 From: Atos1337 Date: Mon, 17 Jan 2022 12:19:35 +0300 Subject: [PATCH 47/74] Fix issues --- controls-mongo/README.md | 26 -------------- .../controls/mongo/AsynchronousMongoClient.kt | 34 ++++++++----------- controls-storage/README.md | 12 +++++++ .../asynchronous/AsynchronousStorageClient.kt | 16 +++++---- .../asynchronous/commonApi.kt | 3 +- .../synchronous/SynchronousStorageClient.kt | 14 +++++--- .../synchronous/commonApi.kt | 2 +- .../controls/storage/asynchronous/jvmApi.kt | 3 +- .../controls/storage/synchronous/jvmApi.kt | 2 +- controls-xodus/README.md | 26 -------------- .../controls/xodus/SynchronousXodusClient.kt | 12 +++---- .../npm/xodus/serialization/json/encoder.kt | 5 +-- 12 files changed, 60 insertions(+), 95 deletions(-) delete mode 100644 controls-mongo/README.md create mode 100644 controls-storage/README.md delete mode 100644 controls-xodus/README.md diff --git a/controls-mongo/README.md b/controls-mongo/README.md deleted file mode 100644 index a627574..0000000 --- a/controls-mongo/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Description -This module allows you to store [DeviceMessages](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt) -from certain [DeviceManager](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt) -or [MagixMessages](magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt) -from [magix server](/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt) -in [mongoDB](https://www.mongodb.com/). - -# Usage - -All usage examples can be found in [VirtualCarController](/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt). - -## Storage from Device Manager - -Just call storeMessagesInXodus. For more details, you can see comments in [source code](/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt) - -## Storage from Magix Server - -Just pass such lambda as parameter to startMagixServer: -```kotlin -{ flow -> - // some code - storeInMongo(flow) - // some code -} -``` -For more details, you can see comments in [source code](/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/connections.kt) diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/AsynchronousMongoClient.kt b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/AsynchronousMongoClient.kt index eb4eb1d..f9c4407 100644 --- a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/AsynchronousMongoClient.kt +++ b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/AsynchronousMongoClient.kt @@ -1,6 +1,7 @@ package ru.mipt.npm.controls.mongo import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json import kotlinx.serialization.serializer import org.litote.kmongo.coroutine.CoroutineClient @@ -31,28 +32,23 @@ internal class AsynchronousMongoClient( private val client: CoroutineClient, private val meta: Meta = Meta.EMPTY ) : AsynchronousStorageClient { - @OptIn(InternalSerializationApi::class) - override suspend fun storeValue(value: T, storageKind: StorageKind, clazz: KClass) { - when (storageKind) { - StorageKind.DEVICE_HUB -> { - val collection = client - .getDatabase( - meta[MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY]?.string - ?: DEFAULT_DEVICE_MESSAGE_DATABASE_NAME - ) - .getCollection() + override suspend fun storeValueInDeviceHub(value: T, serializer: KSerializer) { + val collection = client + .getDatabase( + meta[MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY]?.string + ?: DEFAULT_DEVICE_MESSAGE_DATABASE_NAME + ) + .getCollection() - collection.insertOne(Json.encodeToString(clazz.serializer(), value)) - } + collection.insertOne(Json.encodeToString(serializer, value)) + } - StorageKind.MAGIX_SERVER -> { - val collection = client - .getDatabase(meta[MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY]?.string ?: DEFAULT_MAGIX_MESSAGE_DATABASE_NAME) - .getCollection() + override suspend fun storeValueInMagixServer(value: T, serializer: KSerializer) { + val collection = client + .getDatabase(meta[MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY]?.string ?: DEFAULT_MAGIX_MESSAGE_DATABASE_NAME) + .getCollection() - collection.insertOne(Json.encodeToString(clazz.serializer(), value)) - } - } + collection.insertOne(Json.encodeToString(serializer, value)) } override suspend fun getPropertyHistory( diff --git a/controls-storage/README.md b/controls-storage/README.md new file mode 100644 index 0000000..14289a1 --- /dev/null +++ b/controls-storage/README.md @@ -0,0 +1,12 @@ +# Description + +This module provides API to store [DeviceMessages](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt) +from certain [DeviceManager](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt) +or [MagixMessages](magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt) +from certain [magix server](/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt). + +# Usage + +All usage examples can be found in [VirtualCarController](/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt). + +For more details, you can see comments in source code of this module. diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt index b14f1f8..fb0adc1 100644 --- a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt +++ b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt @@ -1,16 +1,20 @@ package ru.mipt.npm.controls.storage.asynchronous import kotlinx.io.core.Closeable +import kotlinx.serialization.KSerializer +import kotlinx.serialization.serializer import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.storage.synchronous.StorageKind -import ru.mipt.npm.controls.storage.synchronous.SynchronousStorageClient -import kotlin.reflect.KClass public interface AsynchronousStorageClient : Closeable { - public suspend fun storeValue(value: T, storageKind: StorageKind, clazz: KClass) + public suspend fun storeValueInDeviceHub(value: T, serializer: KSerializer) + + public suspend fun storeValueInMagixServer(value: T, serializer: KSerializer) public suspend fun getPropertyHistory(sourceDeviceName: String, propertyName: String): List } -public suspend inline fun AsynchronousStorageClient.storeValue(value: T, storageKind: StorageKind): Unit = - storeValue(value, storageKind, T::class) +public suspend inline fun AsynchronousStorageClient.storeValueInDeviceHub(value: T): Unit = + storeValueInDeviceHub(value, serializer()) + +public suspend inline fun AsynchronousStorageClient.storeValueInMagixServer(value: T): Unit = + storeValueInMagixServer(value, serializer()) diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt index 3877daf..d67ae89 100644 --- a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt +++ b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt @@ -10,7 +10,6 @@ import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.hubMessageFlow -import ru.mipt.npm.controls.storage.synchronous.StorageKind import space.kscience.dataforge.context.Factory import space.kscience.dataforge.context.debug import space.kscience.dataforge.context.logger @@ -29,7 +28,7 @@ public fun DeviceManager.storeMessages( logger.debug { "Storage client created" } return hubMessageFlow(context).filter(filterCondition).onEach { message -> - client.storeValue(message, StorageKind.DEVICE_HUB) + client.storeValueInDeviceHub(message) }.launchIn(context).apply { invokeOnCompletion(onCancelling = true) { client.close() diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt index ef4e22f..aac13de 100644 --- a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt +++ b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt @@ -1,14 +1,20 @@ package ru.mipt.npm.controls.storage.synchronous import kotlinx.io.core.Closeable +import kotlinx.serialization.KSerializer +import kotlinx.serialization.serializer import ru.mipt.npm.controls.api.PropertyChangedMessage -import kotlin.reflect.KClass public interface SynchronousStorageClient : Closeable { - public fun storeValue(value: T, storageKind: StorageKind, clazz: KClass) + public fun storeValueInDeviceHub(value: T, serializer: KSerializer) + + public fun storeValueInMagixServer(value: T, serializer: KSerializer) public fun getPropertyHistory(sourceDeviceName: String, propertyName: String): List } -public inline fun SynchronousStorageClient.storeValue(value: T, storageKind: StorageKind): Unit = - storeValue(value, storageKind, T::class) +public inline fun SynchronousStorageClient.storeValueInDeviceHub(value: T): Unit = + storeValueInDeviceHub(value, serializer()) + +public inline fun SynchronousStorageClient.storeValueInMagixServer(value: T): Unit = + storeValueInMagixServer(value, serializer()) diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt index 29155dc..613b1e9 100644 --- a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt +++ b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt @@ -34,7 +34,7 @@ public fun DeviceManager.storeMessages( logger.debug { "Storage client created" } return hubMessageFlow(context).filter(filterCondition).onEach { message -> - client.storeValue(message, StorageKind.DEVICE_HUB) + client.storeValueInDeviceHub(message) }.launchIn(context).apply { invokeOnCompletion(onCancelling = true) { client.close() diff --git a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/asynchronous/jvmApi.kt b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/asynchronous/jvmApi.kt index 61c634c..ccf65ff 100644 --- a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/asynchronous/jvmApi.kt +++ b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/asynchronous/jvmApi.kt @@ -7,7 +7,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.job -import ru.mipt.npm.controls.storage.synchronous.StorageKind import ru.mipt.npm.magix.server.GenericMagixMessage import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta @@ -21,7 +20,7 @@ internal fun Flow.store( flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, ) { filter(flowFilter).onEach { message -> - client.storeValue(message, StorageKind.MAGIX_SERVER) + client.storeValueInMagixServer(message) } } diff --git a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/synchronous/jvmApi.kt b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/synchronous/jvmApi.kt index 6c31500..0f26915 100644 --- a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/synchronous/jvmApi.kt +++ b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/synchronous/jvmApi.kt @@ -16,7 +16,7 @@ internal fun Flow.store( flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, ) { filter(flowFilter).onEach { message -> - client.storeValue(message, StorageKind.MAGIX_SERVER) + client.storeValueInMagixServer(message) } } diff --git a/controls-xodus/README.md b/controls-xodus/README.md deleted file mode 100644 index dca332c..0000000 --- a/controls-xodus/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Description -This module allows you to store [DeviceMessages](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt) -from certain [DeviceManager](/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt) -or [MagixMessages](magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt) -from [magix server](/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt) -in [xodus database](https://github.com/JetBrains/xodus). - -# Usage - -All usage examples can be found in [VirtualCarController](/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt). - -## Storage from Device Manager - -Just call connectMongo. For more details, you can see comments in [source code](/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt) - -## Storage from Magix Server - -Just pass such lambda as parameter to startMagixServer: -```kotlin -{ flow -> - // some code - storeInXodus(flow) - // some code -} -``` -For more details, you can see comments in [source code](/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/connections.kt) diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/SynchronousXodusClient.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/SynchronousXodusClient.kt index 344d34e..8bca4d2 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/SynchronousXodusClient.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/SynchronousXodusClient.kt @@ -3,6 +3,7 @@ package ru.mipt.npm.controls.xodus import jetbrains.exodus.entitystore.PersistentEntityStore import jetbrains.exodus.entitystore.PersistentEntityStores import kotlinx.datetime.Instant +import kotlinx.serialization.KSerializer import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.storage.synchronous.StorageKind import ru.mipt.npm.controls.storage.synchronous.SynchronousStorageClient @@ -23,13 +24,12 @@ private const val DEVICE_HUB_ENTITY_TYPE = "DeviceMessage" private const val MAGIX_SERVER_ENTITY_TYPE = "MagixMessage" internal class SynchronousXodusClient(private val entityStore: PersistentEntityStore) : SynchronousStorageClient { - override fun storeValue(value: T, storageKind: StorageKind, clazz: KClass) { - val entityType = when (storageKind) { - StorageKind.DEVICE_HUB -> DEVICE_HUB_ENTITY_TYPE - StorageKind.MAGIX_SERVER -> MAGIX_SERVER_ENTITY_TYPE - } + override fun storeValueInDeviceHub(value: T, serializer: KSerializer) { + entityStore.encodeToEntity(value, DEVICE_HUB_ENTITY_TYPE, serializer) + } - entityStore.encodeToEntity(value, entityType, clazz) + override fun storeValueInMagixServer(value: T, serializer: KSerializer) { + entityStore.encodeToEntity(value, MAGIX_SERVER_ENTITY_TYPE, serializer) } override fun getPropertyHistory( diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt index 6a160da..879b2e5 100644 --- a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt +++ b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt @@ -5,6 +5,7 @@ import jetbrains.exodus.entitystore.EntityId import jetbrains.exodus.entitystore.PersistentEntityStore import jetbrains.exodus.entitystore.StoreTransaction import kotlinx.serialization.InternalSerializationApi +import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationStrategy import kotlinx.serialization.json.* import kotlinx.serialization.serializer @@ -73,5 +74,5 @@ public inline fun PersistentEntityStore.encodeToEntity(value: T, ent encodeToEntity(serializer(), value, entityType) @OptIn(InternalSerializationApi::class) -public fun PersistentEntityStore.encodeToEntity(value: T, entityType: String, clazz: KClass): EntityId = encodeToEntity( - clazz.serializer(), value, entityType) +public fun PersistentEntityStore.encodeToEntity(value: T, entityType: String, serializer: KSerializer): EntityId = + encodeToEntity(serializer, value, entityType) From 786e1637b48329b18d3fc50d27075699f178fd69 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 18 Jan 2022 17:37:25 +0300 Subject: [PATCH 48/74] Fix ktor imports --- .space/CODEOWNERS | 1 + controls-core/build.gradle.kts | 2 +- controls-storage/build.gradle.kts | 3 --- .../asynchronous/AsynchronousStorageClient.kt | 2 +- .../asynchronous/commonApi.kt | 2 +- .../synchronous/SynchronousStorageClient.kt | 2 +- .../synchronous/commonApi.kt | 6 ++++-- gradle.properties | 12 ++++++++++++ settings.gradle.kts | 12 ++++++++---- 9 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 .space/CODEOWNERS create mode 100644 gradle.properties diff --git a/.space/CODEOWNERS b/.space/CODEOWNERS new file mode 100644 index 0000000..9f836ea --- /dev/null +++ b/.space/CODEOWNERS @@ -0,0 +1 @@ +./space/* "Project Admin" diff --git a/controls-core/build.gradle.kts b/controls-core/build.gradle.kts index 53769c9..bef9b0a 100644 --- a/controls-core/build.gradle.kts +++ b/controls-core/build.gradle.kts @@ -17,7 +17,7 @@ kotlin { commonMain{ dependencies { api("space.kscience:dataforge-io:$dataforgeVersion") - api(npm.kotlinx.datetime) + api(npmlibs.kotlinx.datetime) } } } diff --git a/controls-storage/build.gradle.kts b/controls-storage/build.gradle.kts index beec52d..13e3d5a 100644 --- a/controls-storage/build.gradle.kts +++ b/controls-storage/build.gradle.kts @@ -4,14 +4,12 @@ plugins { } val dataforgeVersion: String by rootProject.extra -val kotlinx_io_version = "0.1.1" kotlin { sourceSets { commonMain { dependencies { implementation(projects.controlsCore) - implementation("org.jetbrains.kotlinx:kotlinx-io:$kotlinx_io_version") } } @@ -20,7 +18,6 @@ kotlin { implementation(projects.magix.magixApi) implementation(projects.controlsMagixClient) implementation(projects.magix.magixServer) - implementation("org.jetbrains.kotlinx:kotlinx-io-jvm:$kotlinx_io_version") } } } diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt index fb0adc1..71b5558 100644 --- a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt +++ b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt @@ -1,6 +1,6 @@ package ru.mipt.npm.controls.storage.asynchronous -import kotlinx.io.core.Closeable +import io.ktor.utils.io.core.Closeable import kotlinx.serialization.KSerializer import kotlinx.serialization.serializer import ru.mipt.npm.controls.api.PropertyChangedMessage diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt index d67ae89..26f6ddb 100644 --- a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt +++ b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt @@ -1,11 +1,11 @@ package ru.mipt.npm.controls.storage.asynchronous +import io.ktor.utils.io.core.use import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.io.core.use import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.controllers.DeviceManager diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt index aac13de..3b6cfdf 100644 --- a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt +++ b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt @@ -1,6 +1,6 @@ package ru.mipt.npm.controls.storage.synchronous -import kotlinx.io.core.Closeable +import io.ktor.utils.io.core.Closeable import kotlinx.serialization.KSerializer import kotlinx.serialization.serializer import ru.mipt.npm.controls.api.PropertyChangedMessage diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt index 613b1e9..7a074f6 100644 --- a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt +++ b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt @@ -1,9 +1,11 @@ package ru.mipt.npm.controls.storage.synchronous +import io.ktor.utils.io.core.use import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.* -import kotlinx.io.core.use +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage import ru.mipt.npm.controls.controllers.DeviceManager diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..fbaacb0 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,12 @@ +kotlin.code.style=official +kotlin.mpp.stability.nowarn=true + +kotlin.jupyter.add.scanner=false + +org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G +org.gradle.parallel=true + +publishing.github=false +publishing.sonatype=false + +toolsVersion=0.10.9-kotlin-1.6.10 \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 827b689..6a4062d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,13 +1,13 @@ rootProject.name = "controls-kt" - enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") enableFeaturePreview("VERSION_CATALOGS") pluginManagement { - val toolsVersion = "0.10.5" + val toolsVersion: String by extra repositories { + mavenLocal() maven("https://repo.kotlin.link") mavenCentral() gradlePluginPortal() @@ -22,14 +22,18 @@ pluginManagement { } dependencyResolutionManagement { + + val toolsVersion: String by extra + repositories { + mavenLocal() maven("https://repo.kotlin.link") mavenCentral() } versionCatalogs { - create("npm") { - from("ru.mipt.npm:version-catalog:0.10.5") + create("npmlibs") { + from("ru.mipt.npm:version-catalog:$toolsVersion") } } } From 427ecbf91a3f3c7d612dba2ab6d91fe0004e60c8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 18 Jan 2022 18:25:40 +0300 Subject: [PATCH 49/74] Refactor storage --- controls-mongo/build.gradle.kts | 8 +-- ...ousMongoClient.kt => MongoEventStorage.kt} | 27 +++++----- controls-storage/build.gradle.kts | 12 +++-- .../asynchronous/commonApi.kt | 50 ------------------- .../synchronous/SynchronousStorageClient.kt | 20 -------- .../npm/controls/storage/EventStorage.kt} | 8 +-- .../npm/controls/storage/storageCommon.kt} | 24 +++++---- .../controls/storage/asynchronous/jvmApi.kt | 40 --------------- .../{synchronous/jvmApi.kt => storageJvm.kt} | 12 +++-- controls-xodus/build.gradle.kts | 13 +++-- ...ousXodusClient.kt => XodusEventStorage.kt} | 44 ++++++++-------- .../src/test/kotlin/PropertyHistoryTest.kt | 18 ++++--- .../controls/demo/car/VirtualCarController.kt | 14 +++--- 13 files changed, 98 insertions(+), 192 deletions(-) rename controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/{AsynchronousMongoClient.kt => MongoEventStorage.kt} (75%) delete mode 100644 controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt delete mode 100644 controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt rename controls-storage/src/commonMain/kotlin/{ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt => ru/mipt/npm/controls/storage/EventStorage.kt} (64%) rename controls-storage/src/commonMain/kotlin/{ru.mipt.npm.controls.storage/synchronous/commonApi.kt => ru/mipt/npm/controls/storage/storageCommon.kt} (90%) delete mode 100644 controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/asynchronous/jvmApi.kt rename controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/{synchronous/jvmApi.kt => storageJvm.kt} (86%) rename controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/{SynchronousXodusClient.kt => XodusEventStorage.kt} (52%) diff --git a/controls-mongo/build.gradle.kts b/controls-mongo/build.gradle.kts index d5c81f0..c9e6b57 100644 --- a/controls-mongo/build.gradle.kts +++ b/controls-mongo/build.gradle.kts @@ -6,10 +6,10 @@ plugins { val kmongoVersion = "4.4.0" dependencies { - implementation(projects.controlsCore) - implementation(projects.magix.magixApi) - implementation(projects.controlsMagixClient) - implementation(projects.magix.magixServer) implementation(projects.controlsStorage) implementation("org.litote.kmongo:kmongo-coroutine-serialization:$kmongoVersion") } + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE +} diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/AsynchronousMongoClient.kt b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt similarity index 75% rename from controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/AsynchronousMongoClient.kt rename to controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt index f9c4407..f6cb44f 100644 --- a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/AsynchronousMongoClient.kt +++ b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt @@ -1,37 +1,33 @@ package ru.mipt.npm.controls.mongo -import kotlinx.serialization.InternalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json -import kotlinx.serialization.serializer import org.litote.kmongo.coroutine.CoroutineClient import org.litote.kmongo.coroutine.coroutine import org.litote.kmongo.coroutine.insertOne import org.litote.kmongo.reactivestreams.KMongo import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.storage.asynchronous.AsynchronousStorageClient -import ru.mipt.npm.controls.storage.synchronous.StorageKind +import ru.mipt.npm.controls.storage.EventStorage import ru.mipt.npm.magix.server.GenericMagixMessage import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string -import space.kscience.dataforge.context.Factory import space.kscience.dataforge.names.Name -import kotlin.reflect.KClass private const val DEFAULT_DEVICE_MESSAGE_DATABASE_NAME: String = "deviceMessage" private const val DEFAULT_MAGIX_MESSAGE_DATABASE_NAME = "magixMessage" private const val DEFAULT_MONGO_DATABASE_URL = "mongodb://mongoadmin:secret@localhost:27888" -public val MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY: Name = Name.of("mongo", "deviceMessageDatabaseName") +private val MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY: Name = Name.of("mongo", "deviceMessageDatabaseName") public val MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY: Name = Name.of("mongo", "magixMessageDatabaseName") public val MONGO_DATABASE_URL_PROPERTY: Name = Name.of("mongo", "databaseUrl") -internal class AsynchronousMongoClient( +internal class MongoEventStorage( private val client: CoroutineClient, - private val meta: Meta = Meta.EMPTY -) : AsynchronousStorageClient { + private val meta: Meta = Meta.EMPTY, +) : EventStorage { override suspend fun storeValueInDeviceHub(value: T, serializer: KSerializer) { val collection = client .getDatabase( @@ -45,7 +41,8 @@ internal class AsynchronousMongoClient( override suspend fun storeValueInMagixServer(value: T, serializer: KSerializer) { val collection = client - .getDatabase(meta[MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY]?.string ?: DEFAULT_MAGIX_MESSAGE_DATABASE_NAME) + .getDatabase(meta[MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY]?.string + ?: DEFAULT_MAGIX_MESSAGE_DATABASE_NAME) .getCollection() collection.insertOne(Json.encodeToString(serializer, value)) @@ -53,7 +50,7 @@ internal class AsynchronousMongoClient( override suspend fun getPropertyHistory( sourceDeviceName: String, - propertyName: String + propertyName: String, ): List { TODO("Not yet implemented: problems with deserialization") } @@ -63,12 +60,12 @@ internal class AsynchronousMongoClient( } } -public object DefaultAsynchronousMongoClientFactory : Factory { - override fun invoke(meta: Meta, context: Context): AsynchronousStorageClient { +public object DefaultAsynchronousMongoClientFactory : Factory { + override fun invoke(meta: Meta, context: Context): EventStorage { val client = meta[MONGO_DATABASE_URL_PROPERTY]?.string?.let { KMongo.createClient(it).coroutine } ?: KMongo.createClient(DEFAULT_MONGO_DATABASE_URL).coroutine - return AsynchronousMongoClient(client, meta) + return MongoEventStorage(client, meta) } } diff --git a/controls-storage/build.gradle.kts b/controls-storage/build.gradle.kts index 13e3d5a..ab25e1d 100644 --- a/controls-storage/build.gradle.kts +++ b/controls-storage/build.gradle.kts @@ -9,16 +9,20 @@ kotlin { sourceSets { commonMain { dependencies { - implementation(projects.controlsCore) + api(projects.controlsCore) } } jvmMain { dependencies { - implementation(projects.magix.magixApi) - implementation(projects.controlsMagixClient) - implementation(projects.magix.magixServer) + api(projects.magix.magixApi) + api(projects.controlsMagixClient) + api(projects.magix.magixServer) } } } } + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE +} diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt deleted file mode 100644 index 26f6ddb..0000000 --- a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/commonApi.kt +++ /dev/null @@ -1,50 +0,0 @@ -package ru.mipt.npm.controls.storage.asynchronous - -import io.ktor.utils.io.core.use -import kotlinx.coroutines.InternalCoroutinesApi -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.controllers.DeviceManager -import ru.mipt.npm.controls.controllers.hubMessageFlow -import space.kscience.dataforge.context.Factory -import space.kscience.dataforge.context.debug -import space.kscience.dataforge.context.logger -import space.kscience.dataforge.meta.Meta - -/** - * Asynchronous version of synchronous API, so for more details check relative docs - */ - -@OptIn(InternalCoroutinesApi::class) -public fun DeviceManager.storeMessages( - factory: Factory, - filterCondition: suspend (DeviceMessage) -> Boolean = { true }, -): Job { - val client = factory(meta, context) - logger.debug { "Storage client created" } - - return hubMessageFlow(context).filter(filterCondition).onEach { message -> - client.storeValueInDeviceHub(message) - }.launchIn(context).apply { - invokeOnCompletion(onCancelling = true) { - client.close() - logger.debug { "Storage client closed" } - } - } -} - -public suspend fun getPropertyHistory( - sourceDeviceName: String, - propertyName: String, - factory: Factory, - meta: Meta = Meta.EMPTY -): List { - return factory(meta).use { - it.getPropertyHistory(sourceDeviceName, propertyName) - } -} - diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt b/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt deleted file mode 100644 index 3b6cfdf..0000000 --- a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/SynchronousStorageClient.kt +++ /dev/null @@ -1,20 +0,0 @@ -package ru.mipt.npm.controls.storage.synchronous - -import io.ktor.utils.io.core.Closeable -import kotlinx.serialization.KSerializer -import kotlinx.serialization.serializer -import ru.mipt.npm.controls.api.PropertyChangedMessage - -public interface SynchronousStorageClient : Closeable { - public fun storeValueInDeviceHub(value: T, serializer: KSerializer) - - public fun storeValueInMagixServer(value: T, serializer: KSerializer) - - public fun getPropertyHistory(sourceDeviceName: String, propertyName: String): List -} - -public inline fun SynchronousStorageClient.storeValueInDeviceHub(value: T): Unit = - storeValueInDeviceHub(value, serializer()) - -public inline fun SynchronousStorageClient.storeValueInMagixServer(value: T): Unit = - storeValueInMagixServer(value, serializer()) diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/EventStorage.kt similarity index 64% rename from controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt rename to controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/EventStorage.kt index 71b5558..6f874aa 100644 --- a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/asynchronous/AsynchronousStorageClient.kt +++ b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/EventStorage.kt @@ -1,11 +1,11 @@ -package ru.mipt.npm.controls.storage.asynchronous +package ru.mipt.npm.controls.storage import io.ktor.utils.io.core.Closeable import kotlinx.serialization.KSerializer import kotlinx.serialization.serializer import ru.mipt.npm.controls.api.PropertyChangedMessage -public interface AsynchronousStorageClient : Closeable { +public interface EventStorage : Closeable { public suspend fun storeValueInDeviceHub(value: T, serializer: KSerializer) public suspend fun storeValueInMagixServer(value: T, serializer: KSerializer) @@ -13,8 +13,8 @@ public interface AsynchronousStorageClient : Closeable { public suspend fun getPropertyHistory(sourceDeviceName: String, propertyName: String): List } -public suspend inline fun AsynchronousStorageClient.storeValueInDeviceHub(value: T): Unit = +public suspend inline fun EventStorage.storeValueInDeviceHub(value: T): Unit = storeValueInDeviceHub(value, serializer()) -public suspend inline fun AsynchronousStorageClient.storeValueInMagixServer(value: T): Unit = +public suspend inline fun EventStorage.storeValueInMagixServer(value: T): Unit = storeValueInMagixServer(value, serializer()) diff --git a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt similarity index 90% rename from controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt rename to controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt index 7a074f6..1446438 100644 --- a/controls-storage/src/commonMain/kotlin/ru.mipt.npm.controls.storage/synchronous/commonApi.kt +++ b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.storage.synchronous +package ru.mipt.npm.controls.storage import io.ktor.utils.io.core.use import kotlinx.coroutines.InternalCoroutinesApi @@ -14,11 +14,7 @@ import space.kscience.dataforge.context.Factory import space.kscience.dataforge.context.debug import space.kscience.dataforge.context.logger import space.kscience.dataforge.meta.Meta - -public enum class StorageKind { - DEVICE_HUB, - MAGIX_SERVER -} +import kotlin.jvm.JvmName /** * Begin to store DeviceMessages from this DeviceManager @@ -28,8 +24,9 @@ public enum class StorageKind { * @return Job which responsible for our storage */ @OptIn(InternalCoroutinesApi::class) +@JvmName("storeMessagesAsync") public fun DeviceManager.storeMessages( - factory: Factory, + factory: Factory, filterCondition: suspend (DeviceMessage) -> Boolean = { true }, ): Job { val client = factory(meta, context) @@ -51,13 +48,20 @@ public fun DeviceManager.storeMessages( * @param propertyName a name of property, history of which we want to get * @param factory a factory that produce mongo clients */ -public fun getPropertyHistory( +public suspend fun getPropertyHistory( sourceDeviceName: String, propertyName: String, - factory: Factory, - meta: Meta = Meta.EMPTY + factory: Factory, + meta: Meta = Meta.EMPTY, ): List { return factory(meta).use { it.getPropertyHistory(sourceDeviceName, propertyName) } } + + +public enum class StorageKind { + DEVICE_HUB, + MAGIX_SERVER +} + diff --git a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/asynchronous/jvmApi.kt b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/asynchronous/jvmApi.kt deleted file mode 100644 index ccf65ff..0000000 --- a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/asynchronous/jvmApi.kt +++ /dev/null @@ -1,40 +0,0 @@ -package ru.mipt.npm.controls.storage.asynchronous - -import io.ktor.application.* -import kotlinx.coroutines.InternalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.job -import ru.mipt.npm.magix.server.GenericMagixMessage -import space.kscience.dataforge.context.Factory -import space.kscience.dataforge.meta.Meta - -/** - * Asynchronous version of synchronous API, so for more details check relative docs - */ - -internal fun Flow.store( - client: AsynchronousStorageClient, - flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, -) { - filter(flowFilter).onEach { message -> - client.storeValueInMagixServer(message) - } -} - -@OptIn(InternalCoroutinesApi::class) -public fun Application.store( - flow: MutableSharedFlow, - factory: Factory, - meta: Meta = Meta.EMPTY, - flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, -) { - val client = factory(meta) - - flow.store(client, flowFilter) - coroutineContext.job.invokeOnCompletion(onCancelling = true) { - client.close() - } -} diff --git a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/synchronous/jvmApi.kt b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt similarity index 86% rename from controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/synchronous/jvmApi.kt rename to controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt index 0f26915..4e068f3 100644 --- a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/synchronous/jvmApi.kt +++ b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt @@ -1,6 +1,6 @@ -package ru.mipt.npm.controls.storage.synchronous +package ru.mipt.npm.controls.storage -import io.ktor.application.* +import io.ktor.application.Application import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -11,8 +11,12 @@ import ru.mipt.npm.magix.server.GenericMagixMessage import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta +/** + * Asynchronous version of synchronous API, so for more details check relative docs + */ + internal fun Flow.store( - client: SynchronousStorageClient, + client: EventStorage, flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, ) { filter(flowFilter).onEach { message -> @@ -29,8 +33,8 @@ internal fun Flow.store( @OptIn(InternalCoroutinesApi::class) public fun Application.store( flow: MutableSharedFlow, + factory: Factory, meta: Meta = Meta.EMPTY, - factory: Factory, flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, ) { val client = factory(meta) diff --git a/controls-xodus/build.gradle.kts b/controls-xodus/build.gradle.kts index b07ed6b..2dd89d0 100644 --- a/controls-xodus/build.gradle.kts +++ b/controls-xodus/build.gradle.kts @@ -6,10 +6,15 @@ plugins { val xodusVersion = "1.3.232" dependencies { - implementation(projects.xodusSerialization) - implementation(projects.controlsStorage) - implementation(projects.controlsCore) + api(projects.xodusSerialization) + api(projects.controlsStorage) implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") implementation("org.jetbrains.xodus:xodus-environment:$xodusVersion") implementation("org.jetbrains.xodus:xodus-vfs:$xodusVersion") -} \ No newline at end of file + + testImplementation(npmlibs.kotlinx.coroutines.test) +} + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE +} diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/SynchronousXodusClient.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusEventStorage.kt similarity index 52% rename from controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/SynchronousXodusClient.kt rename to controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusEventStorage.kt index 8bca4d2..3072141 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/SynchronousXodusClient.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusEventStorage.kt @@ -5,8 +5,7 @@ import jetbrains.exodus.entitystore.PersistentEntityStores import kotlinx.datetime.Instant import kotlinx.serialization.KSerializer import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.storage.synchronous.StorageKind -import ru.mipt.npm.controls.storage.synchronous.SynchronousStorageClient +import ru.mipt.npm.controls.storage.EventStorage import ru.mipt.npm.xodus.serialization.json.decodeFromEntity import ru.mipt.npm.xodus.serialization.json.encodeToEntity import space.kscience.dataforge.context.Context @@ -15,7 +14,6 @@ import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name -import kotlin.reflect.KClass private const val DEFAULT_XODUS_STORE_PATH = ".storage" public val XODUS_STORE_PROPERTY: Name = Name.of("xodus", "entityStorePath") @@ -23,31 +21,38 @@ public val XODUS_STORE_PROPERTY: Name = Name.of("xodus", "entityStorePath") private const val DEVICE_HUB_ENTITY_TYPE = "DeviceMessage" private const val MAGIX_SERVER_ENTITY_TYPE = "MagixMessage" -internal class SynchronousXodusClient(private val entityStore: PersistentEntityStore) : SynchronousStorageClient { - override fun storeValueInDeviceHub(value: T, serializer: KSerializer) { +public class XodusEventStorage(private val entityStore: PersistentEntityStore) : EventStorage { + override suspend fun storeValueInDeviceHub(value: T, serializer: KSerializer) { entityStore.encodeToEntity(value, DEVICE_HUB_ENTITY_TYPE, serializer) } - override fun storeValueInMagixServer(value: T, serializer: KSerializer) { + override suspend fun storeValueInMagixServer(value: T, serializer: KSerializer) { entityStore.encodeToEntity(value, MAGIX_SERVER_ENTITY_TYPE, serializer) } - override fun getPropertyHistory( + override suspend fun getPropertyHistory( sourceDeviceName: String, - propertyName: String - ): List { - return entityStore.computeInTransaction { txn -> - txn.find(DEVICE_HUB_ENTITY_TYPE, "type", "property.changed").asSequence() - .filter { it?.getProperty("sourceDevice")?.let { it == sourceDeviceName } ?: false && - it?.getProperty("property")?.let { it == propertyName } ?: false - }.sortedByDescending { it?.getProperty("time")?.let { timeStr -> Instant.parse(timeStr as String) } } - .toList().map { txn.decodeFromEntity(it) } - } + propertyName: String, + ): List = entityStore.computeInTransaction { txn -> + txn.find(DEVICE_HUB_ENTITY_TYPE, "type", "property.changed") + .filter { + it?.getProperty("sourceDevice") == sourceDeviceName && it.getProperty("property") == propertyName + } + .sortedByDescending { it?.getProperty("time")?.let { timeStr -> Instant.parse(timeStr as String) } } + .map { txn.decodeFromEntity(it, PropertyChangedMessage.serializer()) } + .toList() } override fun close() { entityStore.close() } + + public companion object : Factory { + override fun invoke(meta: Meta, context: Context): EventStorage { + val entityStore = context.getPersistentEntityStore(meta) + return XodusEventStorage(entityStore) + } + } } private fun Context.getPersistentEntityStore(meta: Meta = Meta.EMPTY): PersistentEntityStore { @@ -57,10 +62,3 @@ private fun Context.getPersistentEntityStore(meta: Meta = Meta.EMPTY): Persisten return PersistentEntityStores.newInstance(storePath) } - -public object DefaultSynchronousXodusClientFactory : Factory { - override fun invoke(meta: Meta, context: Context): SynchronousStorageClient { - val entityStore = context.getPersistentEntityStore(meta) - return SynchronousXodusClient(entityStore) - } -} diff --git a/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt b/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt index 4df90ba..b1d8080 100644 --- a/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt +++ b/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt @@ -1,4 +1,6 @@ import jetbrains.exodus.entitystore.PersistentEntityStores +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Assertions.assertEquals @@ -6,9 +8,9 @@ import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.storage.synchronous.getPropertyHistory -import ru.mipt.npm.controls.xodus.DefaultSynchronousXodusClientFactory +import ru.mipt.npm.controls.storage.getPropertyHistory import ru.mipt.npm.controls.xodus.XODUS_STORE_PROPERTY +import ru.mipt.npm.controls.xodus.XodusEventStorage import ru.mipt.npm.xodus.serialization.json.encodeToEntity import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.Name @@ -56,11 +58,15 @@ internal class PropertyHistoryTest { } } + @OptIn(ExperimentalCoroutinesApi::class) @Test - fun getPropertyHistoryTest() { - assertEquals(listOf(propertyChangedMessages[0]), getPropertyHistory( - "virtual-car", "speed", DefaultSynchronousXodusClientFactory, Meta { + fun getPropertyHistoryTest() = runTest { + assertEquals( + listOf(propertyChangedMessages[0]), + getPropertyHistory( + "virtual-car", "speed", XodusEventStorage, Meta { XODUS_STORE_PROPERTY put storeName - })) + }) + ) } } \ No newline at end of file diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index d13c8ef..9b9a61e 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -14,12 +14,10 @@ import ru.mipt.npm.controls.controllers.DeviceManager import ru.mipt.npm.controls.controllers.install import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration import ru.mipt.npm.controls.mongo.DefaultAsynchronousMongoClientFactory -import ru.mipt.npm.controls.storage.asynchronous.store -import ru.mipt.npm.controls.storage.asynchronous.storeMessages -import ru.mipt.npm.controls.storage.synchronous.store -import ru.mipt.npm.controls.storage.synchronous.storeMessages -import ru.mipt.npm.controls.xodus.DefaultSynchronousXodusClientFactory +import ru.mipt.npm.controls.storage.store +import ru.mipt.npm.controls.storage.storeMessages import ru.mipt.npm.controls.xodus.XODUS_STORE_PROPERTY +import ru.mipt.npm.controls.xodus.XodusEventStorage import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.rsocket.rSocketWithTcp import ru.mipt.npm.magix.server.startMagixServer @@ -57,14 +55,14 @@ class VirtualCarController : Controller(), ContextAware { //starting magix event loop and connect it to entity store magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> - store( flow, Meta { + store(flow, XodusEventStorage, Meta { XODUS_STORE_PROPERTY put VirtualCarControllerConfig.magixEntityStorePath.toString() - }, DefaultSynchronousXodusClientFactory) + }) store(flow, DefaultAsynchronousMongoClientFactory) } magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) //connect to device entity store - xodusStorageJob = deviceManager.storeMessages(DefaultSynchronousXodusClientFactory) + xodusStorageJob = deviceManager.storeMessages(XodusEventStorage) //Create mongo client and connect to MongoDB mongoStorageJob = deviceManager.storeMessages(DefaultAsynchronousMongoClientFactory) //Launch device client and connect it to the server From 2e7eefeba97b2728f058b5de40c05dbfe98c2213 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 18 Jan 2022 18:39:19 +0300 Subject: [PATCH 50/74] rename methods --- .../ru/mipt/npm/controls/mongo/MongoEventStorage.kt | 4 ++-- .../ru/mipt/npm/controls/storage/EventStorage.kt | 12 ++++++------ .../ru/mipt/npm/controls/storage/storageCommon.kt | 2 +- .../ru/mipt/npm/controls/storage/storageJvm.kt | 2 +- .../ru/mipt/npm/controls/xodus/XodusEventStorage.kt | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt index f6cb44f..4bfbafb 100644 --- a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt +++ b/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt @@ -28,7 +28,7 @@ internal class MongoEventStorage( private val client: CoroutineClient, private val meta: Meta = Meta.EMPTY, ) : EventStorage { - override suspend fun storeValueInDeviceHub(value: T, serializer: KSerializer) { + override suspend fun storeDeviceMessage(value: T, serializer: KSerializer) { val collection = client .getDatabase( meta[MONGO_DEVICE_MESSAGE_DATABASE_NAME_PROPERTY]?.string @@ -39,7 +39,7 @@ internal class MongoEventStorage( collection.insertOne(Json.encodeToString(serializer, value)) } - override suspend fun storeValueInMagixServer(value: T, serializer: KSerializer) { + override suspend fun storeMagixMessage(value: T, serializer: KSerializer) { val collection = client .getDatabase(meta[MONGO_MAGIX_MESSAGE_DATABASE_NAME_PROPERTY]?.string ?: DEFAULT_MAGIX_MESSAGE_DATABASE_NAME) diff --git a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/EventStorage.kt b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/EventStorage.kt index 6f874aa..2388d05 100644 --- a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/EventStorage.kt +++ b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/EventStorage.kt @@ -6,15 +6,15 @@ import kotlinx.serialization.serializer import ru.mipt.npm.controls.api.PropertyChangedMessage public interface EventStorage : Closeable { - public suspend fun storeValueInDeviceHub(value: T, serializer: KSerializer) + public suspend fun storeDeviceMessage(value: T, serializer: KSerializer) - public suspend fun storeValueInMagixServer(value: T, serializer: KSerializer) + public suspend fun storeMagixMessage(value: T, serializer: KSerializer) public suspend fun getPropertyHistory(sourceDeviceName: String, propertyName: String): List } -public suspend inline fun EventStorage.storeValueInDeviceHub(value: T): Unit = - storeValueInDeviceHub(value, serializer()) +public suspend inline fun EventStorage.storeDeviceMessage(value: T): Unit = + storeDeviceMessage(value, serializer()) -public suspend inline fun EventStorage.storeValueInMagixServer(value: T): Unit = - storeValueInMagixServer(value, serializer()) +public suspend inline fun EventStorage.storeMagixMessage(value: T): Unit = + storeMagixMessage(value, serializer()) diff --git a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt index 1446438..d035bc0 100644 --- a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt +++ b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt @@ -33,7 +33,7 @@ public fun DeviceManager.storeMessages( logger.debug { "Storage client created" } return hubMessageFlow(context).filter(filterCondition).onEach { message -> - client.storeValueInDeviceHub(message) + client.storeDeviceMessage(message) }.launchIn(context).apply { invokeOnCompletion(onCancelling = true) { client.close() diff --git a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt index 4e068f3..cf09ceb 100644 --- a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt +++ b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt @@ -20,7 +20,7 @@ internal fun Flow.store( flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, ) { filter(flowFilter).onEach { message -> - client.storeValueInMagixServer(message) + client.storeMagixMessage(message) } } diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusEventStorage.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusEventStorage.kt index 3072141..6e1e30a 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusEventStorage.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusEventStorage.kt @@ -22,11 +22,11 @@ private const val DEVICE_HUB_ENTITY_TYPE = "DeviceMessage" private const val MAGIX_SERVER_ENTITY_TYPE = "MagixMessage" public class XodusEventStorage(private val entityStore: PersistentEntityStore) : EventStorage { - override suspend fun storeValueInDeviceHub(value: T, serializer: KSerializer) { + override suspend fun storeDeviceMessage(value: T, serializer: KSerializer) { entityStore.encodeToEntity(value, DEVICE_HUB_ENTITY_TYPE, serializer) } - override suspend fun storeValueInMagixServer(value: T, serializer: KSerializer) { + override suspend fun storeMagixMessage(value: T, serializer: KSerializer) { entityStore.encodeToEntity(value, MAGIX_SERVER_ENTITY_TYPE, serializer) } From 3d0b888c11d9316248d8a4eb0a7a290b7f21602e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 23 May 2022 23:30:38 +0300 Subject: [PATCH 51/74] Update dependencies --- build.gradle.kts | 4 +- .../npm/controls/opcua/client/MiloDevice.kt | 4 +- controls-server/build.gradle.kts | 8 +-- .../npm/controls/server/deviceWebServer.kt | 47 ++++++++-------- .../ru/mipt/npm/controls/server/responses.kt | 4 +- .../mipt/npm/controls/storage/storageJvm.kt | 2 +- controls-tcp/build.gradle.kts | 17 ++---- .../ru/mipt/npm/controls/ports/KtorTcpPort.kt | 3 +- demo/build.gradle.kts | 10 ++-- demo/car/build.gradle.kts | 10 ++-- .../npm/controls/demo/demoDeviceServer.kt | 53 ++++++++++--------- .../controls/demo/generateMessageSchema.kt | 14 ++--- gradle.properties | 3 +- gradle/wrapper/gradle-wrapper.properties | 2 +- magix/magix-java-client/build.gradle.kts | 5 ++ magix/magix-rsocket/build.gradle.kts | 8 ++- .../npm/magix/rsocket/RSocketMagixEndpoint.kt | 9 ++-- .../ru/mipt/npm/magix/rsocket/withTcp.kt | 5 +- magix/magix-server/build.gradle.kts | 11 ++-- .../ru/mipt/npm/magix/server/magixModule.kt | 47 ++++++++-------- .../kotlin/ru/mipt/npm/magix/server/server.kt | 23 ++++---- .../kotlin/ru/mipt/npm/magix/server/sse.kt | 7 ++- .../devices/pimotionmaster/piDebugServer.kt | 4 +- settings.gradle.kts | 8 +-- 24 files changed, 152 insertions(+), 156 deletions(-) rename controls-tcp/src/{jvmMain => main}/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt (97%) diff --git a/build.gradle.kts b/build.gradle.kts index 6d7df71..8a4a56f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,9 +2,9 @@ plugins { id("ru.mipt.npm.gradle.project") } -val dataforgeVersion: String by extra("0.5.1") +val dataforgeVersion: String by extra("0.5.2") val ktorVersion: String by extra(ru.mipt.npm.gradle.KScienceVersions.ktorVersion) -val rsocketVersion by extra("0.13.1") +val rsocketVersion by extra("0.15.4") allprojects { group = "ru.mipt.npm" diff --git a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDevice.kt b/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDevice.kt index de56325..04393fc 100644 --- a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDevice.kt +++ b/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDevice.kt @@ -39,8 +39,8 @@ public suspend inline fun MiloDevice.readOpcWithTime( val time = data.serverTime ?: error("No server time provided") val meta: Meta = when (val content = data.value.value) { is T -> return content to time - content is Meta -> content as Meta - content is ExtensionObject -> (content as ExtensionObject).decode(client.dynamicSerializationContext) as Meta + is Meta -> content + is ExtensionObject -> content.decode(client.dynamicSerializationContext) as Meta else -> error("Incompatible OPC property value $content") } diff --git a/controls-server/build.gradle.kts b/controls-server/build.gradle.kts index 7eba824..710550c 100644 --- a/controls-server/build.gradle.kts +++ b/controls-server/build.gradle.kts @@ -15,7 +15,9 @@ dependencies { implementation(project(":controls-tcp")) implementation(projects.magix.magixServer) implementation("io.ktor:ktor-server-cio:$ktorVersion") - implementation("io.ktor:ktor-websockets:$ktorVersion") - implementation("io.ktor:ktor-serialization:$ktorVersion") - implementation("io.ktor:ktor-html-builder:$ktorVersion") + implementation("io.ktor:ktor-server-websockets:$ktorVersion") + implementation("io.ktor:ktor-server-content-negotiation:$ktorVersion") + implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") + implementation("io.ktor:ktor-server-html-builder:$ktorVersion") + implementation("io.ktor:ktor-server-status-pages:$ktorVersion") } \ No newline at end of file diff --git a/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt b/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt index f896a24..c04c378 100644 --- a/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt +++ b/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt @@ -1,24 +1,23 @@ package ru.mipt.npm.controls.server -import io.ktor.application.* -import io.ktor.features.CORS -import io.ktor.features.StatusPages -import io.ktor.html.respondHtml import io.ktor.http.HttpStatusCode -import io.ktor.request.receiveText -import io.ktor.response.respond -import io.ktor.response.respondRedirect -import io.ktor.response.respondText -import io.ktor.routing.get -import io.ktor.routing.post -import io.ktor.routing.route -import io.ktor.routing.routing +import io.ktor.server.application.* import io.ktor.server.cio.CIO import io.ktor.server.engine.ApplicationEngine import io.ktor.server.engine.embeddedServer -import io.ktor.util.getValue -import io.ktor.websocket.WebSockets +import io.ktor.server.html.respondHtml +import io.ktor.server.plugins.statuspages.StatusPages +import io.ktor.server.request.receiveText +import io.ktor.server.response.respond +import io.ktor.server.response.respondRedirect +import io.ktor.server.response.respondText +import io.ktor.server.routing.get +import io.ktor.server.routing.post +import io.ktor.server.routing.route +import io.ktor.server.routing.routing +import io.ktor.server.util.getValue +import io.ktor.server.websocket.WebSockets import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.html.* @@ -52,11 +51,11 @@ public fun CoroutineScope.startDeviceServer( return this.embeddedServer(CIO, port, host) { install(WebSockets) - install(CORS) { - anyHost() - } +// install(CORS) { +// anyHost() +// } install(StatusPages) { - exception { cause -> + exception { call, cause -> call.respond(HttpStatusCode.BadRequest, cause.message ?: "") } } @@ -83,15 +82,15 @@ public fun Application.deviceManagerModule( rawSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_RAW_PORT, buffer: Int = 100, ) { - if (featureOrNull(WebSockets) == null) { + if (pluginOrNull(WebSockets) == null) { install(WebSockets) } - if (featureOrNull(CORS) == null) { - install(CORS) { - anyHost() - } - } +// if (pluginOrNull(CORS) == null) { +// install(CORS) { +// anyHost() +// } +// } routing { route(route) { diff --git a/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/responses.kt b/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/responses.kt index 8041acb..fe56b96 100644 --- a/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/responses.kt +++ b/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/responses.kt @@ -1,8 +1,8 @@ package ru.mipt.npm.controls.server -import io.ktor.application.ApplicationCall import io.ktor.http.ContentType -import io.ktor.response.respondText +import io.ktor.server.application.ApplicationCall +import io.ktor.server.response.respondText import kotlinx.serialization.json.JsonObjectBuilder import kotlinx.serialization.json.buildJsonObject import ru.mipt.npm.controls.api.DeviceMessage diff --git a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt index cf09ceb..28135dc 100644 --- a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt +++ b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt @@ -1,6 +1,6 @@ package ru.mipt.npm.controls.storage -import io.ktor.application.Application +import io.ktor.server.application.Application import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow diff --git a/controls-tcp/build.gradle.kts b/controls-tcp/build.gradle.kts index 88d2928..4ed2905 100644 --- a/controls-tcp/build.gradle.kts +++ b/controls-tcp/build.gradle.kts @@ -1,17 +1,10 @@ plugins { - id("ru.mipt.npm.gradle.mpp") + id("ru.mipt.npm.gradle.jvm") } val ktorVersion: String by rootProject.extra -kotlin { - sourceSets { - commonMain { - dependencies { - api(project(":controls-core")) - api("io.ktor:ktor-network:$ktorVersion") - } - } - - } -} \ No newline at end of file +dependencies { + api(project(":controls-core")) + api("io.ktor:ktor-network:$ktorVersion") +} diff --git a/controls-tcp/src/jvmMain/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt b/controls-tcp/src/main/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt similarity index 97% rename from controls-tcp/src/jvmMain/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt rename to controls-tcp/src/main/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt index d3f48d3..c9227d5 100644 --- a/controls-tcp/src/jvmMain/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt +++ b/controls-tcp/src/main/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt @@ -16,7 +16,6 @@ import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.string -import java.net.InetSocketAddress import kotlin.coroutines.CoroutineContext public class KtorTcpPort internal constructor( @@ -29,7 +28,7 @@ public class KtorTcpPort internal constructor( override fun toString(): String = "port[tcp:$host:$port]" private val futureSocket = scope.async { - aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().connect(InetSocketAddress(host, port)) + aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().connect(host, port) } private val writeChannel = scope.async { diff --git a/demo/build.gradle.kts b/demo/build.gradle.kts index 522357e..f4c06ef 100644 --- a/demo/build.gradle.kts +++ b/demo/build.gradle.kts @@ -1,15 +1,13 @@ plugins { kotlin("jvm") - id("org.openjfx.javafxplugin") version "0.0.9" + id("org.openjfx.javafxplugin") version "0.0.10" application } repositories { mavenCentral() - jcenter() maven("https://repo.kotlin.link") - maven("https://kotlin.bintray.com/kotlinx") } val ktorVersion: String by rootProject.extra @@ -26,9 +24,9 @@ dependencies { implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("no.tornado:tornadofx:1.7.20") - implementation("space.kscience:plotlykt-server:0.5.0-dev-1") - implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6") - implementation("ch.qos.logback:logback-classic:1.2.3") + implementation("space.kscience:plotlykt-server:0.5.2-dev-2") +// implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6") + implementation("ch.qos.logback:logback-classic:1.2.11") } tasks.withType().configureEach { diff --git a/demo/car/build.gradle.kts b/demo/car/build.gradle.kts index 93968e7..5bce76e 100644 --- a/demo/car/build.gradle.kts +++ b/demo/car/build.gradle.kts @@ -26,8 +26,8 @@ dependencies { implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1") implementation("no.tornado:tornadofx:1.7.20") - implementation("space.kscience:plotlykt-server:0.5.0-dev-1") - implementation("ch.qos.logback:logback-classic:1.2.3") + implementation("space.kscience:plotlykt-server:0.5.0") + implementation("ch.qos.logback:logback-classic:1.2.11") implementation("org.jetbrains.xodus:xodus-entity-store:1.3.232") implementation("org.jetbrains.xodus:xodus-environment:1.3.232") implementation("org.jetbrains.xodus:xodus-vfs:1.3.232") @@ -46,6 +46,6 @@ javafx { modules("javafx.controls") } -//application { -// mainClass.set("ru.mipt.npm.controls.demo.DemoControllerViewKt") -//} \ No newline at end of file +application { + mainClass.set("ru.mipt.npm.controls.demo.car.VirtualCarControllerKt") +} \ No newline at end of file diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt index 8d6f95b..7f65add 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt @@ -1,12 +1,12 @@ package ru.mipt.npm.controls.demo -import io.ktor.application.install -import io.ktor.features.CORS +import io.ktor.server.application.install import io.ktor.server.cio.CIO import io.ktor.server.engine.ApplicationEngine import io.ktor.server.engine.embeddedServer -import io.ktor.websocket.WebSockets -import io.rsocket.kotlin.transport.ktor.server.RSocketSupport +import io.ktor.server.plugins.cors.routing.CORS +import io.ktor.server.websocket.WebSockets +import io.rsocket.kotlin.ktor.server.RSocketSupport import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.html.div @@ -54,33 +54,32 @@ suspend fun Trace.updateXYFrom(flow: Flow>>) { } -suspend fun MagixEndpoint.startDemoDeviceServer(): ApplicationEngine = - embeddedServer(CIO, 9090) { - install(WebSockets) - install(RSocketSupport) +suspend fun MagixEndpoint.startDemoDeviceServer(): ApplicationEngine = embeddedServer(CIO, 9090) { + install(WebSockets) + install(RSocketSupport) - install(CORS) { - anyHost() - } + install(CORS) { + anyHost() + } - val sinFlow = MutableSharedFlow()// = device.sin.flow() - val cosFlow = MutableSharedFlow()// = device.cos.flow() + val sinFlow = MutableSharedFlow()// = device.sin.flow() + val cosFlow = MutableSharedFlow()// = device.cos.flow() - launch { - subscribe().collect { magix -> - (magix.payload as? PropertyChangedMessage)?.let { message -> - when (message.property) { - "sin" -> sinFlow.emit(message.value) - "cos" -> cosFlow.emit(message.value) - } + launch { + subscribe().collect { magix -> + (magix.payload as? PropertyChangedMessage)?.let { message -> + when (message.property) { + "sin" -> sinFlow.emit(message.value) + "cos" -> cosFlow.emit(message.value) } } } + } - plotlyModule().apply { - updateMode = PlotlyUpdateMode.PUSH - updateInterval = 50 - }.page { container -> + plotlyModule{ + updateMode = PlotlyUpdateMode.PUSH + updateInterval = 50 + page { container -> val sinCosFlow = sinFlow.zip(cosFlow) { sin, cos -> sin.double!! to cos.double!! } @@ -140,6 +139,8 @@ suspend fun MagixEndpoint.startDemoDeviceServer(): ApplicationEng } } } - } - }.apply { start() } + + } + } +}.apply { start() } diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/generateMessageSchema.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/generateMessageSchema.kt index 420bca3..85522a5 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/generateMessageSchema.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/generateMessageSchema.kt @@ -1,10 +1,10 @@ package ru.mipt.npm.controls.demo -import com.github.ricky12awesome.jss.encodeToSchema -import com.github.ricky12awesome.jss.globalJson -import ru.mipt.npm.controls.api.DeviceMessage +//import com.github.ricky12awesome.jss.encodeToSchema +//import com.github.ricky12awesome.jss.globalJson +//import ru.mipt.npm.controls.api.DeviceMessage -fun main() { - val schema = globalJson.encodeToSchema(DeviceMessage.serializer(), generateDefinitions = false) - println(schema) -} \ No newline at end of file +//fun main() { +// val schema = globalJson.encodeToSchema(DeviceMessage.serializer(), generateDefinitions = false) +// println(schema) +//} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index fbaacb0..7077cd1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,10 +3,9 @@ kotlin.mpp.stability.nowarn=true kotlin.jupyter.add.scanner=false -org.gradle.jvmargs=-XX:MaxMetaspaceSize=1G org.gradle.parallel=true publishing.github=false publishing.sonatype=false -toolsVersion=0.10.9-kotlin-1.6.10 \ No newline at end of file +toolsVersion=0.11.5-kotlin-1.6.21 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..aa991fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/magix/magix-java-client/build.gradle.kts b/magix/magix-java-client/build.gradle.kts index 5e34637..d250b3f 100644 --- a/magix/magix-java-client/build.gradle.kts +++ b/magix/magix-java-client/build.gradle.kts @@ -8,3 +8,8 @@ dependencies { implementation(project(":magix:magix-rsocket")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:${ru.mipt.npm.gradle.KScienceVersions.coroutinesVersion}") } + +java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 +} diff --git a/magix/magix-rsocket/build.gradle.kts b/magix/magix-rsocket/build.gradle.kts index 1e0647c..3ded3e8 100644 --- a/magix/magix-rsocket/build.gradle.kts +++ b/magix/magix-rsocket/build.gradle.kts @@ -22,7 +22,13 @@ kotlin { dependencies { api(projects.magix.magixApi) implementation("io.ktor:ktor-client-core:$ktorVersion") - implementation("io.rsocket.kotlin:rsocket-transport-ktor-client:$rsocketVersion") + implementation("io.rsocket.kotlin:rsocket-ktor-client:$rsocketVersion") + } + } + jvmMain{ + dependencies{ + implementation("io.ktor:ktor-network:$ktorVersion") + implementation("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") } } } diff --git a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt index 490ddaf..7fdcee4 100644 --- a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt +++ b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt @@ -1,16 +1,17 @@ package ru.mipt.npm.magix.rsocket import io.ktor.client.HttpClient -import io.ktor.client.features.websocket.WebSockets +import io.ktor.client.plugins.websocket.WebSockets import io.rsocket.kotlin.RSocket import io.rsocket.kotlin.core.RSocketConnector import io.rsocket.kotlin.core.RSocketConnectorBuilder +import io.rsocket.kotlin.ktor.client.RSocketSupport +import io.rsocket.kotlin.ktor.client.rSocket import io.rsocket.kotlin.payload.buildPayload import io.rsocket.kotlin.payload.data -import io.rsocket.kotlin.transport.ktor.client.RSocketSupport -import io.rsocket.kotlin.transport.ktor.client.rSocket import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map @@ -79,7 +80,7 @@ public suspend fun MagixEndpoint.Companion.rSocketWithWebSockets( val rSocket = client.rSocket(host, port, path) //Ensure client is closed after rSocket if finished - rSocket.job.invokeOnCompletion { + rSocket.coroutineContext[Job]?.invokeOnCompletion { client.close() } diff --git a/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt b/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt index 90c2ffa..d3c0719 100644 --- a/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt +++ b/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt @@ -1,11 +1,9 @@ package ru.mipt.npm.magix.rsocket -import io.ktor.network.selector.ActorSelectorManager import io.ktor.network.sockets.SocketOptions import io.ktor.util.InternalAPI import io.rsocket.kotlin.core.RSocketConnectorBuilder -import io.rsocket.kotlin.transport.ktor.TcpClientTransport -import kotlinx.coroutines.Dispatchers +import io.rsocket.kotlin.transport.ktor.tcp.TcpClientTransport import kotlinx.serialization.KSerializer import ru.mipt.npm.magix.api.MagixEndpoint import kotlin.coroutines.coroutineContext @@ -23,7 +21,6 @@ public suspend fun MagixEndpoint.Companion.rSocketWithTcp( rSocketConfig: RSocketConnectorBuilder.ConnectionConfigBuilder.() -> Unit = {}, ): RSocketMagixEndpoint { val transport = TcpClientTransport( - ActorSelectorManager(Dispatchers.IO), hostname = host, port = port, configure = tcpConfig diff --git a/magix/magix-server/build.gradle.kts b/magix/magix-server/build.gradle.kts index 7f01ea8..5215f19 100644 --- a/magix/magix-server/build.gradle.kts +++ b/magix/magix-server/build.gradle.kts @@ -21,12 +21,13 @@ val ktorVersion: String = ru.mipt.npm.gradle.KScienceVersions.ktorVersion dependencies{ api(project(":magix:magix-api")) api("io.ktor:ktor-server-cio:$ktorVersion") - api("io.ktor:ktor-websockets:$ktorVersion") - api("io.ktor:ktor-serialization:$ktorVersion") - api("io.ktor:ktor-html-builder:$ktorVersion") + api("io.ktor:ktor-server-websockets:$ktorVersion") + api("io.ktor:ktor-server-content-negotiation:$ktorVersion") + api("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") + api("io.ktor:ktor-server-html-builder:$ktorVersion") - api("io.rsocket.kotlin:rsocket-core:$rsocketVersion") - api("io.rsocket.kotlin:rsocket-transport-ktor-server:$rsocketVersion") + api("io.rsocket.kotlin:rsocket-ktor-server:$rsocketVersion") + api("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") api("org.zeromq:jeromq:0.5.2") } \ No newline at end of file diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt index 3211243..a19c8a4 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt +++ b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt @@ -1,24 +1,23 @@ package ru.mipt.npm.magix.server -import io.ktor.application.* -import io.ktor.features.CORS -import io.ktor.features.ContentNegotiation -import io.ktor.html.respondHtml -import io.ktor.request.receive -import io.ktor.routing.get -import io.ktor.routing.post -import io.ktor.routing.route -import io.ktor.routing.routing -import io.ktor.serialization.json -import io.ktor.util.getValue -import io.ktor.websocket.WebSockets +import io.ktor.serialization.kotlinx.json.json +import io.ktor.server.application.* +import io.ktor.server.html.respondHtml +import io.ktor.server.plugins.contentnegotiation.ContentNegotiation +import io.ktor.server.request.receive +import io.ktor.server.routing.get +import io.ktor.server.routing.post +import io.ktor.server.routing.route +import io.ktor.server.routing.routing +import io.ktor.server.util.getValue +import io.ktor.server.websocket.WebSockets import io.rsocket.kotlin.ConnectionAcceptor import io.rsocket.kotlin.RSocketRequestHandler +import io.rsocket.kotlin.ktor.server.RSocketSupport +import io.rsocket.kotlin.ktor.server.rSocket import io.rsocket.kotlin.payload.Payload import io.rsocket.kotlin.payload.buildPayload import io.rsocket.kotlin.payload.data -import io.rsocket.kotlin.transport.ktor.server.RSocketSupport -import io.rsocket.kotlin.transport.ktor.server.rSocket import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* import kotlinx.html.* @@ -88,23 +87,23 @@ private fun ApplicationCall.buildFilter(): MagixMessageFilter { * Attache magix http/sse and websocket-based rsocket event loop + statistics page to existing [MutableSharedFlow] */ public fun Application.magixModule(magixFlow: MutableSharedFlow, route: String = "/") { - if (featureOrNull(WebSockets) == null) { + if (pluginOrNull(WebSockets) == null) { install(WebSockets) } - if (featureOrNull(CORS) == null) { - install(CORS) { - //TODO consider more safe policy - anyHost() - } - } - if (featureOrNull(ContentNegotiation) == null) { +// if (pluginOrNull(CORS) == null) { +// install(CORS) { +// //TODO consider more safe policy +// anyHost() +// } +// } + if (pluginOrNull(ContentNegotiation) == null) { install(ContentNegotiation) { json() } } - if (featureOrNull(RSocketSupport) == null) { + if (pluginOrNull(RSocketSupport) == null) { install(RSocketSupport) } @@ -150,7 +149,7 @@ public fun Application.magixModule(magixFlow: MutableSharedFlow, - rawSocketPort: Int = DEFAULT_MAGIX_RAW_PORT -): Job { - val tcpTransport = TcpServerTransport(ActorSelectorManager(Dispatchers.IO), port = rawSocketPort) - val rSocketJob = RSocketServer().bind(tcpTransport, magixAcceptor(magixFlow)) + rawSocketPort: Int = DEFAULT_MAGIX_RAW_PORT, +): TcpServer { + val tcpTransport = TcpServerTransport(port = rawSocketPort) + val rSocketJob: TcpServer = RSocketServer().bindIn(this, tcpTransport, magixAcceptor(magixFlow)) + coroutineContext[Job]?.invokeOnCompletion { - rSocketJob.cancel() + rSocketJob.handlerJob.cancel() } + return rSocketJob; } @@ -42,7 +41,7 @@ public fun CoroutineScope.startMagixServer( buffer: Int = 100, enableRawRSocket: Boolean = true, enableZmq: Boolean = true, - applicationConfiguration: Application.(MutableSharedFlow) -> Unit = {} + applicationConfiguration: Application.(MutableSharedFlow) -> Unit = {}, ): ApplicationEngine { val logger = LoggerFactory.getLogger("magix-server") val magixFlow = MutableSharedFlow( diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/sse.kt b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/sse.kt index a1c057f..c771ab0 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/sse.kt +++ b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/sse.kt @@ -1,14 +1,13 @@ package ru.mipt.npm.magix.server -import io.ktor.application.ApplicationCall import io.ktor.http.CacheControl import io.ktor.http.ContentType -import io.ktor.response.cacheControl -import io.ktor.response.respondBytesWriter +import io.ktor.server.application.ApplicationCall +import io.ktor.server.response.cacheControl +import io.ktor.server.response.respondBytesWriter import io.ktor.utils.io.ByteWriteChannel import io.ktor.utils.io.writeStringUtf8 import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.collect /** * The data class representing a SSE Event that will be sent to the client. diff --git a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/piDebugServer.kt b/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/piDebugServer.kt index f5a4c81..021dff5 100644 --- a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/piDebugServer.kt +++ b/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/piDebugServer.kt @@ -8,10 +8,8 @@ import io.ktor.util.InternalAPI import io.ktor.util.moveToByteArray import io.ktor.utils.io.writeAvailable import kotlinx.coroutines.* -import kotlinx.coroutines.flow.collect import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Global -import java.net.InetSocketAddress val exceptionHandler = CoroutineExceptionHandler { _, throwable -> throwable.printStackTrace() @@ -20,7 +18,7 @@ val exceptionHandler = CoroutineExceptionHandler { _, throwable -> @OptIn(InternalAPI::class) fun Context.launchPiDebugServer(port: Int, axes: List): Job = launch(exceptionHandler) { val virtualDevice = PiMotionMasterVirtualDevice(this@launchPiDebugServer, axes) - val server = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().bind(InetSocketAddress("localhost", port)) + val server = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().bind("localhost", port) println("Started virtual port server at ${server.localAddress}") while (isActive) { diff --git a/settings.gradle.kts b/settings.gradle.kts index 6a4062d..7a8cfae 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,9 +8,9 @@ pluginManagement { repositories { mavenLocal() - maven("https://repo.kotlin.link") - mavenCentral() gradlePluginPortal() + mavenCentral() + maven("https://repo.kotlin.link") } plugins { @@ -27,8 +27,8 @@ dependencyResolutionManagement { repositories { mavenLocal() - maven("https://repo.kotlin.link") mavenCentral() + maven("https://repo.kotlin.link") } versionCatalogs { @@ -45,7 +45,7 @@ include( ":controls-server", ":controls-opcua", ":demo", - ":demo:car", +// ":demo:car", ":magix", ":magix:magix-api", ":magix:magix-server", From ba4d2abd27bac89168c37f2cfa92d12f0bcfe876 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 24 May 2022 21:28:42 +0300 Subject: [PATCH 52/74] Add native targets --- .../src/main/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt | 2 +- magix/magix-api/build.gradle.kts | 1 + magix/magix-rsocket/build.gradle.kts | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/controls-tcp/src/main/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt b/controls-tcp/src/main/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt index c9227d5..ca2cf61 100644 --- a/controls-tcp/src/main/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt +++ b/controls-tcp/src/main/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt @@ -37,7 +37,7 @@ public class KtorTcpPort internal constructor( private val listenerJob = scope.launch { val input = futureSocket.await().openReadChannel() - input.consumeEachBufferRange { buffer, last -> + input.consumeEachBufferRange { buffer, _ -> val array = ByteArray(buffer.remaining()) buffer.get(array) receive(array) diff --git a/magix/magix-api/build.gradle.kts b/magix/magix-api/build.gradle.kts index c6eb397..7a11b09 100644 --- a/magix/magix-api/build.gradle.kts +++ b/magix/magix-api/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("ru.mipt.npm.gradle.mpp") + id("ru.mipt.npm.gradle.native") `maven-publish` } diff --git a/magix/magix-rsocket/build.gradle.kts b/magix/magix-rsocket/build.gradle.kts index 3ded3e8..3d445a0 100644 --- a/magix/magix-rsocket/build.gradle.kts +++ b/magix/magix-rsocket/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("ru.mipt.npm.gradle.mpp") + id("ru.mipt.npm.gradle.native") `maven-publish` } From b6f37695290700f3df6860410c1e59f84a100fa0 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 1 Jun 2022 10:11:12 +0300 Subject: [PATCH 53/74] Refactor xodus storage for local history --- controls-core/build.gradle.kts | 2 +- .../{controllers => manager}/DeviceManager.kt | 2 +- .../deviceMessages.kt | 2 +- .../ru/mipt/npm/controls/client/dfMagix.kt | 18 +-- .../ru/mipt/npm/controls/client/tangoMagix.kt | 2 +- controls-mongo/build.gradle.kts | 2 +- .../controls/opcua/server/DeviceNameSpace.kt | 2 +- .../npm/controls/server/deviceWebServer.kt | 4 +- .../controls/storage/DeviceMessageStorage.kt | 23 +++ .../mipt/npm/controls/storage/EventStorage.kt | 20 --- .../npm/controls/storage/storageCommon.kt | 79 +++++----- .../npm/controls/storage/workDirectory.kt | 32 ++++ controls-xodus/build.gradle.kts | 7 +- .../xodus/XodusDeviceMessageStorage.kt | 142 ++++++++++++++++++ .../npm/controls/xodus/XodusEventStorage.kt | 64 -------- .../npm/controls/demo/DemoControllerView.kt | 4 +- gradle.properties | 2 +- .../ru/mipt/npm/magix/api/MagixMessage.kt | 4 +- .../ru/mipt/npm/magix/api/MagixRegistry.kt | 17 +++ .../ru/mipt/npm/magix/api/converters.kt | 2 +- magix/magix-demo/src/main/kotlin/zmq.kt | 2 +- .../magix-storage-xodus/build.gradle.kts | 21 +++ .../magix/storage/xodus/XodusMagixStorage.kt | 25 +++ .../pimotionmaster/PiMotionMasterApp.kt | 4 +- settings.gradle.kts | 13 +- xodus-serialization/build.gradle.kts | 22 --- .../npm/xodus/serialization/json/decoder.kt | 62 -------- .../npm/xodus/serialization/json/encoder.kt | 78 ---------- .../mipt/npm/xodus/serialization/json/main.kt | 38 ----- .../serialization/json/EncoderDecoderTests.kt | 105 ------------- 30 files changed, 334 insertions(+), 466 deletions(-) rename controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/{controllers => manager}/DeviceManager.kt (97%) rename controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/{controllers => manager}/deviceMessages.kt (98%) create mode 100644 controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/DeviceMessageStorage.kt delete mode 100644 controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/EventStorage.kt create mode 100644 controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/workDirectory.kt create mode 100644 controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt delete mode 100644 controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusEventStorage.kt create mode 100644 magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixRegistry.kt create mode 100644 magix/magix-storage/magix-storage-xodus/build.gradle.kts create mode 100644 magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt delete mode 100644 xodus-serialization/build.gradle.kts delete mode 100644 xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt delete mode 100644 xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt delete mode 100644 xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt delete mode 100644 xodus-serialization/src/test/kotlin/ru/mipt/npm/xodus/serialization/json/EncoderDecoderTests.kt diff --git a/controls-core/build.gradle.kts b/controls-core/build.gradle.kts index bef9b0a..2510957 100644 --- a/controls-core/build.gradle.kts +++ b/controls-core/build.gradle.kts @@ -6,7 +6,7 @@ plugins { val dataforgeVersion: String by rootProject.extra kscience { - useCoroutines("1.4.1") + useCoroutines() useSerialization{ json() } diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/manager/DeviceManager.kt similarity index 97% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt rename to controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/manager/DeviceManager.kt index 23432ea..3194f78 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/DeviceManager.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/manager/DeviceManager.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.controllers +package ru.mipt.npm.controls.manager import kotlinx.coroutines.launch import ru.mipt.npm.controls.api.Device diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/deviceMessages.kt b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/manager/deviceMessages.kt similarity index 98% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/deviceMessages.kt rename to controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/manager/deviceMessages.kt index 018b3ba..6d47dba 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/controllers/deviceMessages.kt +++ b/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/manager/deviceMessages.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.controllers +package ru.mipt.npm.controls.manager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow diff --git a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt index 8faed41..87972c9 100644 --- a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt @@ -6,9 +6,9 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.controllers.DeviceManager -import ru.mipt.npm.controls.controllers.hubMessageFlow -import ru.mipt.npm.controls.controllers.respondHubMessage +import ru.mipt.npm.controls.manager.DeviceManager +import ru.mipt.npm.controls.manager.hubMessageFlow +import ru.mipt.npm.controls.manager.respondHubMessage import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixMessage import space.kscience.dataforge.context.error @@ -35,11 +35,11 @@ public fun DeviceManager.connectToMagix( val responsePayload = respondHubMessage(request.payload) if (responsePayload != null) { val response = MagixMessage( + origin = endpointID, + payload = responsePayload, format = DATAFORGE_MAGIX_FORMAT, id = generateId(request), - parentId = request.id, - origin = endpointID, - payload = responsePayload + parentId = request.id ) endpoint.broadcast(response) @@ -50,10 +50,10 @@ public fun DeviceManager.connectToMagix( hubMessageFlow(this).onEach { payload -> val magixMessage = MagixMessage( - format = DATAFORGE_MAGIX_FORMAT, - id = "df[${payload.hashCode()}]", origin = endpointID, - payload = payload + payload = payload, + format = DATAFORGE_MAGIX_FORMAT, + id = "df[${payload.hashCode()}]" ) preSendAction(magixMessage) endpoint.broadcast( diff --git a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/tangoMagix.kt b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/tangoMagix.kt index 922098a..b1d9e2d 100644 --- a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/tangoMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/tangoMagix.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.launch import kotlinx.serialization.Serializable import ru.mipt.npm.controls.api.get import ru.mipt.npm.controls.api.getOrReadProperty -import ru.mipt.npm.controls.controllers.DeviceManager +import ru.mipt.npm.controls.manager.DeviceManager import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixMessage import space.kscience.dataforge.context.error diff --git a/controls-mongo/build.gradle.kts b/controls-mongo/build.gradle.kts index c9e6b57..fa27fb3 100644 --- a/controls-mongo/build.gradle.kts +++ b/controls-mongo/build.gradle.kts @@ -3,7 +3,7 @@ plugins { `maven-publish` } -val kmongoVersion = "4.4.0" +val kmongoVersion = "4.5.1" dependencies { implementation(projects.controlsStorage) diff --git a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/DeviceNameSpace.kt b/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/DeviceNameSpace.kt index e187f5e..8b95d8e 100644 --- a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/DeviceNameSpace.kt +++ b/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/DeviceNameSpace.kt @@ -22,7 +22,7 @@ import ru.mipt.npm.controls.api.Device import ru.mipt.npm.controls.api.DeviceHub import ru.mipt.npm.controls.api.PropertyDescriptor import ru.mipt.npm.controls.api.onPropertyChange -import ru.mipt.npm.controls.controllers.DeviceManager +import ru.mipt.npm.controls.manager.DeviceManager import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MetaSerializer import space.kscience.dataforge.names.Name diff --git a/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt b/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt index c04c378..ca1b738 100644 --- a/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt +++ b/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt @@ -30,8 +30,8 @@ import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyGetMessage import ru.mipt.npm.controls.api.PropertySetMessage import ru.mipt.npm.controls.api.getOrNull -import ru.mipt.npm.controls.controllers.DeviceManager -import ru.mipt.npm.controls.controllers.respondHubMessage +import ru.mipt.npm.controls.manager.DeviceManager +import ru.mipt.npm.controls.manager.respondHubMessage import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.server.GenericMagixMessage import ru.mipt.npm.magix.server.launchMagixServerRawRSocket diff --git a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/DeviceMessageStorage.kt b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/DeviceMessageStorage.kt new file mode 100644 index 0000000..7c5a8bb --- /dev/null +++ b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/DeviceMessageStorage.kt @@ -0,0 +1,23 @@ +package ru.mipt.npm.controls.storage + +import kotlinx.datetime.Instant +import ru.mipt.npm.controls.api.DeviceMessage +import space.kscience.dataforge.names.Name + +/** + * A storage for Controls-kt [DeviceMessage] + */ +public interface DeviceMessageStorage { + public suspend fun write(event: DeviceMessage) + + public suspend fun readAll(): List + + public suspend fun read( + eventType: String, + range: ClosedRange? = null, + sourceDevice: Name? = null, + targetDevice: Name? = null, + ): List + + public fun close() +} \ No newline at end of file diff --git a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/EventStorage.kt b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/EventStorage.kt deleted file mode 100644 index 2388d05..0000000 --- a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/EventStorage.kt +++ /dev/null @@ -1,20 +0,0 @@ -package ru.mipt.npm.controls.storage - -import io.ktor.utils.io.core.Closeable -import kotlinx.serialization.KSerializer -import kotlinx.serialization.serializer -import ru.mipt.npm.controls.api.PropertyChangedMessage - -public interface EventStorage : Closeable { - public suspend fun storeDeviceMessage(value: T, serializer: KSerializer) - - public suspend fun storeMagixMessage(value: T, serializer: KSerializer) - - public suspend fun getPropertyHistory(sourceDeviceName: String, propertyName: String): List -} - -public suspend inline fun EventStorage.storeDeviceMessage(value: T): Unit = - storeDeviceMessage(value, serializer()) - -public suspend inline fun EventStorage.storeMagixMessage(value: T): Unit = - storeMagixMessage(value, serializer()) diff --git a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt index d035bc0..9d9c058 100644 --- a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt +++ b/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt @@ -1,20 +1,21 @@ package ru.mipt.npm.controls.storage -import io.ktor.utils.io.core.use -import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.Job import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.controllers.DeviceManager -import ru.mipt.npm.controls.controllers.hubMessageFlow +import ru.mipt.npm.controls.manager.DeviceManager +import ru.mipt.npm.controls.manager.hubMessageFlow import space.kscience.dataforge.context.Factory import space.kscience.dataforge.context.debug import space.kscience.dataforge.context.logger -import space.kscience.dataforge.meta.Meta -import kotlin.jvm.JvmName + +//TODO replace by plugin? +public fun DeviceManager.storage( + factory: Factory, +): DeviceMessageStorage = factory(meta, context) /** * Begin to store DeviceMessages from this DeviceManager @@ -23,45 +24,41 @@ import kotlin.jvm.JvmName * @param filterCondition allow you to specify messages which we want to store. Always true by default. * @return Job which responsible for our storage */ -@OptIn(InternalCoroutinesApi::class) -@JvmName("storeMessagesAsync") public fun DeviceManager.storeMessages( - factory: Factory, + factory: Factory, filterCondition: suspend (DeviceMessage) -> Boolean = { true }, ): Job { - val client = factory(meta, context) - logger.debug { "Storage client created" } + val storage = factory(meta, context) + logger.debug { "Message storage with meta = $meta created" } return hubMessageFlow(context).filter(filterCondition).onEach { message -> - client.storeDeviceMessage(message) - }.launchIn(context).apply { - invokeOnCompletion(onCancelling = true) { - client.close() - logger.debug { "Storage client closed" } - } - } + storage.write(message) + }.onCompletion { + storage.close() + logger.debug { "Message storage closed" } + }.launchIn(context) } -/** - * @return the list of deviceMessages that describes changes of specified property of specified device sorted by time - * @param sourceDeviceName a name of device, history of which property we want to get - * @param propertyName a name of property, history of which we want to get - * @param factory a factory that produce mongo clients - */ -public suspend fun getPropertyHistory( - sourceDeviceName: String, - propertyName: String, - factory: Factory, - meta: Meta = Meta.EMPTY, -): List { - return factory(meta).use { - it.getPropertyHistory(sourceDeviceName, propertyName) - } -} - - -public enum class StorageKind { - DEVICE_HUB, - MAGIX_SERVER -} +///** +// * @return the list of deviceMessages that describes changes of specified property of specified device sorted by time +// * @param sourceDeviceName a name of device, history of which property we want to get +// * @param propertyName a name of property, history of which we want to get +// * @param factory a factory that produce mongo clients +// */ +//public suspend fun getPropertyHistory( +// sourceDeviceName: String, +// propertyName: String, +// factory: Factory, +// meta: Meta = Meta.EMPTY, +//): List { +// return factory(meta).use { +// it.getPropertyHistory(sourceDeviceName, propertyName) +// } +//} +// +// +//public enum class StorageKind { +// DEVICE_HUB, +// MAGIX_SERVER +//} diff --git a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/workDirectory.kt b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/workDirectory.kt new file mode 100644 index 0000000..76d8439 --- /dev/null +++ b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/workDirectory.kt @@ -0,0 +1,32 @@ +package ru.mipt.npm.controls.storage + +import space.kscience.dataforge.context.ContextBuilder +import space.kscience.dataforge.io.IOPlugin +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.set +import space.kscience.dataforge.meta.string +import java.nio.file.Path +import kotlin.io.path.Path + +//TODO remove on DF 0.6 + +internal val IOPlugin.Companion.WORK_DIRECTORY_KEY: String get() = ".dataforge" + +public val IOPlugin.workDirectory: Path + get() { + val workDirectoryPath = meta[IOPlugin.WORK_DIRECTORY_KEY].string + ?: context.properties[IOPlugin.WORK_DIRECTORY_KEY].string + ?: ".dataforge" + + return Path(workDirectoryPath) + } + +public fun ContextBuilder.workDirectory(path: String) { + properties { + set(IOPlugin.WORK_DIRECTORY_KEY, path) + } +} + +public fun ContextBuilder.workDirectory(path: Path){ + workDirectory(path.toAbsolutePath().toString()) +} diff --git a/controls-xodus/build.gradle.kts b/controls-xodus/build.gradle.kts index 2dd89d0..ae99038 100644 --- a/controls-xodus/build.gradle.kts +++ b/controls-xodus/build.gradle.kts @@ -3,14 +3,13 @@ plugins { `maven-publish` } -val xodusVersion = "1.3.232" +val xodusVersion = "2.0.1" dependencies { - api(projects.xodusSerialization) api(projects.controlsStorage) implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") - implementation("org.jetbrains.xodus:xodus-environment:$xodusVersion") - implementation("org.jetbrains.xodus:xodus-vfs:$xodusVersion") +// implementation("org.jetbrains.xodus:xodus-environment:$xodusVersion") +// implementation("org.jetbrains.xodus:xodus-vfs:$xodusVersion") testImplementation(npmlibs.kotlinx.coroutines.test) } diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt new file mode 100644 index 0000000..ea96f36 --- /dev/null +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt @@ -0,0 +1,142 @@ +package ru.mipt.npm.controls.xodus + +import jetbrains.exodus.entitystore.Entity +import jetbrains.exodus.entitystore.PersistentEntityStore +import jetbrains.exodus.entitystore.PersistentEntityStores +import jetbrains.exodus.entitystore.StoreTransaction +import kotlinx.datetime.Instant +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.descriptors.serialDescriptor +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive +import ru.mipt.npm.controls.api.DeviceMessage +import ru.mipt.npm.controls.storage.DeviceMessageStorage +import ru.mipt.npm.controls.storage.workDirectory +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.context.fetch +import space.kscience.dataforge.io.IOPlugin +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.string +import space.kscience.dataforge.misc.DFExperimental +import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.matches +import space.kscience.dataforge.names.parseAsName + + +internal fun StoreTransaction.writeMessage(message: DeviceMessage): Entity { + val entity: Entity = newEntity(XodusDeviceMessageStorage.DEVICE_MESSAGE_ENTITY_TYPE) + val json = Json.encodeToJsonElement(DeviceMessage.serializer(), message).jsonObject + val type = json["type"]?.jsonPrimitive?.content ?: error("Message json representation must have type.") + entity.setProperty("type", type) + + message.sourceDevice?.let { + entity.setProperty(DeviceMessage::sourceDevice.name, it.toString()) + } + message.targetDevice?.let { + entity.setProperty(DeviceMessage::targetDevice.name, it.toString()) + } + message.time?.let { + entity.setProperty(DeviceMessage::targetDevice.name, it.toString()) + } + entity.setBlobString("json", Json.encodeToString(json)) + + return entity +} + + +@OptIn(DFExperimental::class) +private fun Entity.propertyMatchesName(propertyName: String, pattern: Name? = null) = + pattern == null || getProperty(propertyName).toString().parseAsName().matches(pattern) + +private fun Entity.timeInRange(range: ClosedRange?): Boolean { + if (range == null) return true + val time: Instant? = getProperty(DeviceMessage::time.name)?.let { entityString -> + Instant.parse(entityString.toString()) + } + return time != null && time in range +} + +public class XodusDeviceMessageStorage( + private val entityStore: PersistentEntityStore, +) : DeviceMessageStorage, AutoCloseable { + + override suspend fun write(event: DeviceMessage) { + //entityStore.encodeToEntity(event, DEVICE_MESSAGE_ENTITY_TYPE, DeviceMessage.serializer()) + entityStore.computeInTransaction { txn -> + txn.writeMessage(event) + } + } + + override suspend fun readAll(): List = entityStore.computeInTransaction { transaction -> + transaction.getAll( + DEVICE_MESSAGE_ENTITY_TYPE, + ).map { + Json.decodeFromString( + DeviceMessage.serializer(), + it.getBlobString("json") ?: error("No json content found") + ) + } + } + + override suspend fun read( + eventType: String, + range: ClosedRange?, + sourceDevice: Name?, + targetDevice: Name?, + ): List = entityStore.computeInTransaction { transaction -> + transaction.find( + DEVICE_MESSAGE_ENTITY_TYPE, + "type", + eventType + ).mapNotNull { + if (it.timeInRange(range) && + it.propertyMatchesName(DeviceMessage::sourceDevice.name, sourceDevice) && + it.propertyMatchesName(DeviceMessage::targetDevice.name, targetDevice) + ) { + Json.decodeFromString( + DeviceMessage.serializer(), + it.getBlobString("json") ?: error("No json content found") + ) + } else null + } + } + + override fun close() { + entityStore.close() + } + + public companion object : Factory { + internal const val DEVICE_MESSAGE_ENTITY_TYPE = "DeviceMessage" + public val XODUS_STORE_PROPERTY: Name = Name.of("xodus", "storagePath") + + + override fun invoke(meta: Meta, context: Context): XodusDeviceMessageStorage { + val io = context.fetch(IOPlugin) + val storePath = io.workDirectory.resolve( + meta[XODUS_STORE_PROPERTY]?.string + ?: context.properties[XODUS_STORE_PROPERTY]?.string ?: "storage" + ) + + val entityStore = PersistentEntityStores.newInstance(storePath.toFile()) + + return XodusDeviceMessageStorage(entityStore) + } + } +} + +/** + * Query all messages of given type + */ +@OptIn(ExperimentalSerializationApi::class) +public suspend inline fun XodusDeviceMessageStorage.query( + range: ClosedRange? = null, + sourceDevice: Name? = null, + targetDevice: Name? = null, +): List = read(serialDescriptor().serialName, range, sourceDevice, targetDevice).map { + //Check that all types are correct + it as T +} diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusEventStorage.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusEventStorage.kt deleted file mode 100644 index 6e1e30a..0000000 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusEventStorage.kt +++ /dev/null @@ -1,64 +0,0 @@ -package ru.mipt.npm.controls.xodus - -import jetbrains.exodus.entitystore.PersistentEntityStore -import jetbrains.exodus.entitystore.PersistentEntityStores -import kotlinx.datetime.Instant -import kotlinx.serialization.KSerializer -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.storage.EventStorage -import ru.mipt.npm.xodus.serialization.json.decodeFromEntity -import ru.mipt.npm.xodus.serialization.json.encodeToEntity -import space.kscience.dataforge.context.Context -import space.kscience.dataforge.context.Factory -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.string -import space.kscience.dataforge.names.Name - -private const val DEFAULT_XODUS_STORE_PATH = ".storage" -public val XODUS_STORE_PROPERTY: Name = Name.of("xodus", "entityStorePath") - -private const val DEVICE_HUB_ENTITY_TYPE = "DeviceMessage" -private const val MAGIX_SERVER_ENTITY_TYPE = "MagixMessage" - -public class XodusEventStorage(private val entityStore: PersistentEntityStore) : EventStorage { - override suspend fun storeDeviceMessage(value: T, serializer: KSerializer) { - entityStore.encodeToEntity(value, DEVICE_HUB_ENTITY_TYPE, serializer) - } - - override suspend fun storeMagixMessage(value: T, serializer: KSerializer) { - entityStore.encodeToEntity(value, MAGIX_SERVER_ENTITY_TYPE, serializer) - } - - override suspend fun getPropertyHistory( - sourceDeviceName: String, - propertyName: String, - ): List = entityStore.computeInTransaction { txn -> - txn.find(DEVICE_HUB_ENTITY_TYPE, "type", "property.changed") - .filter { - it?.getProperty("sourceDevice") == sourceDeviceName && it.getProperty("property") == propertyName - } - .sortedByDescending { it?.getProperty("time")?.let { timeStr -> Instant.parse(timeStr as String) } } - .map { txn.decodeFromEntity(it, PropertyChangedMessage.serializer()) } - .toList() - } - - override fun close() { - entityStore.close() - } - - public companion object : Factory { - override fun invoke(meta: Meta, context: Context): EventStorage { - val entityStore = context.getPersistentEntityStore(meta) - return XodusEventStorage(entityStore) - } - } -} - -private fun Context.getPersistentEntityStore(meta: Meta = Meta.EMPTY): PersistentEntityStore { - val storePath = meta[XODUS_STORE_PROPERTY]?.string - ?: properties[XODUS_STORE_PROPERTY]?.string - ?: DEFAULT_XODUS_STORE_PATH - - return PersistentEntityStores.newInstance(storePath) -} diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt index 8a273ab..64e0fa0 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt @@ -10,11 +10,11 @@ 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.DemoDevice.Companion.cosScale import ru.mipt.npm.controls.demo.DemoDevice.Companion.sinScale import ru.mipt.npm.controls.demo.DemoDevice.Companion.timeScale +import ru.mipt.npm.controls.manager.DeviceManager +import ru.mipt.npm.controls.manager.install import ru.mipt.npm.controls.opcua.server.OpcUaServer import ru.mipt.npm.controls.opcua.server.endpoint import ru.mipt.npm.controls.opcua.server.serveDevices diff --git a/gradle.properties b/gradle.properties index 7077cd1..bd1c590 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,4 +8,4 @@ org.gradle.parallel=true publishing.github=false publishing.sonatype=false -toolsVersion=0.11.5-kotlin-1.6.21 \ No newline at end of file +toolsVersion=0.11.5-kotlin-1.7.0-RC \ No newline at end of file diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt index eaf2098..e804a69 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt @@ -25,9 +25,9 @@ import kotlinx.serialization.json.JsonElement */ @Serializable public data class MagixMessage( - val format: String, val origin: String, val payload: T, + val format: String = origin, val target: String? = null, val id: String? = null, val parentId: String? = null, @@ -39,4 +39,4 @@ public data class MagixMessage( */ @Suppress("UNCHECKED_CAST") public fun MagixMessage.replacePayload(payloadTransform: (T) -> R): MagixMessage = - MagixMessage(format, origin, payloadTransform(payload), target, id, parentId, user) \ No newline at end of file + MagixMessage(origin, payloadTransform(payload), format, target, id, parentId, user) \ No newline at end of file diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixRegistry.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixRegistry.kt new file mode 100644 index 0000000..a13b9ce --- /dev/null +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixRegistry.kt @@ -0,0 +1,17 @@ +package ru.mipt.npm.magix.api + +import kotlinx.serialization.json.JsonElement + +/** + * An interface to access distributed Magix property registry + */ +public interface MagixRegistry { + /** + * Request a property with name [propertyName] and user authentication data [user]. + * + * Return a property value in its generic form or null if it is not present. + * + * Throw an exception if property is present but access is denied. + */ + public suspend fun request(propertyName: String, user: JsonElement? = null): JsonElement? +} diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt index f1c854c..1ab2de7 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt @@ -18,9 +18,9 @@ public fun CoroutineScope.launchMagixConverter( ): Job = inputEndpoint.subscribe(filter).onEach { message-> val newPayload = transformer(message.payload) val transformed: MagixMessage = MagixMessage( - outputFormat, newOrigin ?: message.origin, newPayload, + outputFormat, message.target, message.id, message.parentId, diff --git a/magix/magix-demo/src/main/kotlin/zmq.kt b/magix/magix-demo/src/main/kotlin/zmq.kt index 2a73640..44bafc9 100644 --- a/magix/magix-demo/src/main/kotlin/zmq.kt +++ b/magix/magix-demo/src/main/kotlin/zmq.kt @@ -23,7 +23,7 @@ suspend fun MagixEndpoint.sendJson( parentId: String? = null, user: JsonElement? = null, builder: JsonObjectBuilder.() -> Unit -): Unit = broadcast(MagixMessage(format, origin, buildJsonObject(builder), target, id, parentId, user)) +): Unit = broadcast(MagixMessage(origin, buildJsonObject(builder), format, target, id, parentId, user)) internal const val numberOfMessages = 100 diff --git a/magix/magix-storage/magix-storage-xodus/build.gradle.kts b/magix/magix-storage/magix-storage-xodus/build.gradle.kts new file mode 100644 index 0000000..2ac7af2 --- /dev/null +++ b/magix/magix-storage/magix-storage-xodus/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + id("ru.mipt.npm.gradle.jvm") + `maven-publish` +} + +val xodusVersion = "2.0.1" + +kscience{ + useCoroutines() +} + +dependencies { + api(projects.magix.magixApi) + implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") + + testImplementation(npmlibs.kotlinx.coroutines.test) +} + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE +} diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt new file mode 100644 index 0000000..007d3b1 --- /dev/null +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt @@ -0,0 +1,25 @@ +package ru.mipt.npm.magix.storage.xodus + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.serialization.json.JsonElement +import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.api.MagixMessageFilter +import java.nio.file.Path + +public class XodusMagixStorage( + private val scope: CoroutineScope, + private val path: Path, + private val endpoint: MagixEndpoint, + private val filter: MagixMessageFilter = MagixMessageFilter(), +) : AutoCloseable { + + private val subscriptionJob = endpoint.subscribe(filter).onEach { + TODO() + }.launchIn(scope) + + override fun close() { + subscriptionJob.cancel() + } +} \ No newline at end of file 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 76fc7d2..1476201 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 @@ -11,8 +11,8 @@ import javafx.scene.layout.VBox import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import ru.mipt.npm.controls.controllers.DeviceManager -import ru.mipt.npm.controls.controllers.installing +import ru.mipt.npm.controls.manager.DeviceManager +import ru.mipt.npm.controls.manager.installing import ru.mipt.npm.devices.pimotionmaster.PiMotionMasterDevice.Axis.Companion.maxPosition import ru.mipt.npm.devices.pimotionmaster.PiMotionMasterDevice.Axis.Companion.minPosition import ru.mipt.npm.devices.pimotionmaster.PiMotionMasterDevice.Axis.Companion.position diff --git a/settings.gradle.kts b/settings.gradle.kts index 7a8cfae..e49920d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -44,8 +44,9 @@ include( ":controls-serial", ":controls-server", ":controls-opcua", - ":demo", -// ":demo:car", + ":controls-xodus", +// ":controls-mongo", + ":controls-storage", ":magix", ":magix:magix-api", ":magix:magix-server", @@ -53,10 +54,10 @@ include( ":magix:magix-java-client", ":magix:magix-zmq", ":magix:magix-demo", +// ":magix:magix-storage", + ":magix:magix-storage:magix-storage-xodus", ":controls-magix-client", ":motors", - ":controls-xodus", - ":controls-mongo", - ":xodus-serialization", - ":controls-storage" + ":demo", +// ":demo:car", ) diff --git a/xodus-serialization/build.gradle.kts b/xodus-serialization/build.gradle.kts deleted file mode 100644 index 40755c2..0000000 --- a/xodus-serialization/build.gradle.kts +++ /dev/null @@ -1,22 +0,0 @@ -plugins { - id("ru.mipt.npm.gradle.jvm") - `maven-publish` -} - -val xodusVersion = "1.3.232" - -//TODO to be moved to DataForge - -kscience { - useSerialization { - json() - } -} - -dependencies { - implementation(projects.magix.magixApi) - implementation(projects.controlsCore) - implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") - implementation("org.jetbrains.xodus:xodus-environment:$xodusVersion") - implementation("org.jetbrains.xodus:xodus-vfs:$xodusVersion") -} diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt deleted file mode 100644 index eeba26b..0000000 --- a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/decoder.kt +++ /dev/null @@ -1,62 +0,0 @@ -package ru.mipt.npm.xodus.serialization.json - -import jetbrains.exodus.entitystore.Entity -import jetbrains.exodus.entitystore.EntityId -import jetbrains.exodus.entitystore.PersistentEntityStore -import jetbrains.exodus.entitystore.StoreTransaction -import kotlinx.serialization.DeserializationStrategy -import kotlinx.serialization.json.* -import kotlinx.serialization.serializer - -internal fun StoreTransaction.decodeFromEntity(entity: Entity): JsonElement = buildJsonObject { - entity.propertyNames.forEach { property -> - entity.getProperty(property).let { value -> - when (value) { - is Number -> put(property, value) - is Boolean -> put(property, value) - is String -> put(property, value) - else -> throw IllegalStateException("Unsupported type for primitive field") - } - } - } - - entity.linkNames.forEach { link -> - entity.getLinks(link).let { entities -> - when (entities.size()) { - 1L -> entities.first?.let { put(link, decodeFromEntity(it)) } - else -> { - putJsonArray(link) { - entities.forEach { - add(decodeFromEntity(it)) - } - } - } - } - } - } -} - -public fun StoreTransaction.decodeFromEntity(entity: Entity, deserializer: DeserializationStrategy): T { - val jsonElement = decodeFromEntity(entity) - val json = Json { ignoreUnknownKeys = true } - return json.decodeFromJsonElement(deserializer, jsonElement) -} - -public inline fun StoreTransaction.decodeFromEntity(entity: Entity): T = decodeFromEntity(entity, serializer()) - -// First entity with entityType will be decoded -public fun PersistentEntityStore.decodeFromEntity(entityType: String, deserializer: DeserializationStrategy): T? { - return computeInTransaction { txn -> - txn.getAll(entityType).first?.let { txn.decodeFromEntity(it, deserializer) } - } -} - -public inline fun PersistentEntityStore.decodeFromEntity(entityType: String): T? = decodeFromEntity(entityType, serializer()) - -public fun PersistentEntityStore.decodeFromEntity(entityId: EntityId, deserializer: DeserializationStrategy): T? { - return computeInTransaction { txn -> - txn.decodeFromEntity(txn.getEntity(entityId), deserializer) - } -} - -public inline fun PersistentEntityStore.decodeFromEntity(entityId: EntityId): T? = decodeFromEntity(entityId, serializer()) diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt deleted file mode 100644 index 879b2e5..0000000 --- a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/encoder.kt +++ /dev/null @@ -1,78 +0,0 @@ -package ru.mipt.npm.xodus.serialization.json - -import jetbrains.exodus.entitystore.Entity -import jetbrains.exodus.entitystore.EntityId -import jetbrains.exodus.entitystore.PersistentEntityStore -import jetbrains.exodus.entitystore.StoreTransaction -import kotlinx.serialization.InternalSerializationApi -import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerializationStrategy -import kotlinx.serialization.json.* -import kotlinx.serialization.serializer -import kotlin.reflect.KClass - -internal fun StoreTransaction.encodeToEntity(jsonElement: JsonElement, entity: Entity) { - when (jsonElement) { - is JsonPrimitive -> throw IllegalStateException("Can't serialize primitive value to entity") - is JsonArray -> throw IllegalStateException("Can't serialize array value to entity") - is JsonObject -> { - jsonElement.forEach { entry -> - entry.value.let { value -> - when(value) { - // не сможем десериализовать, если JsonNull (надо ли обрабатывать???) (можно сохранить в отдельный список ключи null-ов) - is JsonPrimitive -> { - if (value.isString) { - entity.setProperty(entry.key, value.content) - } else { - (value.longOrNull ?: value.doubleOrNull ?: value.booleanOrNull)?.let { - entity.setProperty( - entry.key, - it - ) - } - } - } - - // считаем, что все элементы массива - JsonObject, иначе не можем напрямую сериализовать (надо придывать костыли???) - // не сможем десериализовать, если массив пустой (надо ли обрабатывать???) (можно сохранять в отдельный список ключи пустых массивов) - is JsonArray -> { - value.forEach { element -> - val childEntity = newEntity("${entity.type}.${entry.key}") - encodeToEntity(element, childEntity) - entity.addLink(entry.key, childEntity) - } - } - - is JsonObject -> { - val childEntity = newEntity("${entity.type}.${entry.key}") - encodeToEntity(value, childEntity) - entity.setLink(entry.key, childEntity) - } - } - } - } - } - } -} - -public fun StoreTransaction.encodeToEntity(serializer: SerializationStrategy, value: T, entityType: String): Entity { - val entity: Entity = newEntity(entityType) - encodeToEntity(Json.encodeToJsonElement(serializer, value), entity) - return entity -} - -public inline fun StoreTransaction.encodeToEntity(value: T, entityType: String): Entity = - encodeToEntity(serializer(), value, entityType) - -public fun PersistentEntityStore.encodeToEntity(serializer: SerializationStrategy, value: T, entityType: String): EntityId { - return computeInTransaction { txn -> - txn.encodeToEntity(serializer, value, entityType).id - } -} - -public inline fun PersistentEntityStore.encodeToEntity(value: T, entityType: String): EntityId = - encodeToEntity(serializer(), value, entityType) - -@OptIn(InternalSerializationApi::class) -public fun PersistentEntityStore.encodeToEntity(value: T, entityType: String, serializer: KSerializer): EntityId = - encodeToEntity(serializer, value, entityType) diff --git a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt b/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt deleted file mode 100644 index 60d9bb8..0000000 --- a/xodus-serialization/src/main/kotlin/ru/mipt/npm/xodus/serialization/json/main.kt +++ /dev/null @@ -1,38 +0,0 @@ -package ru.mipt.npm.xodus.serialization.json - -import jetbrains.exodus.entitystore.PersistentEntityStores -import kotlinx.datetime.Instant -import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.magix.api.MagixMessage -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.names.Name -import java.nio.file.Paths - -internal fun main() { - val expectedMessage = MagixMessage( - "dataforge", - "dataforge", - PropertyChangedMessage( - "acceleration", - Meta { - "x" put 3.0 - "y" put 9.0 - }, - Name.parse("virtual-car"), - Name.parse("magix-virtual-car"), - time = Instant.fromEpochMilliseconds(1337) - ), - "magix-virtual-car" - ) - - val entityStore = PersistentEntityStores.newInstance(Paths.get(".xodus_serialization").toString()) - entityStore.executeInTransaction { txn -> - txn.encodeToEntity(expectedMessage, "MagixMessage") - } - - entityStore.executeInTransaction { txn -> - txn.getAll("MagixMessage").first?.let { println(txn.decodeFromEntity>(it) == expectedMessage) } - } -} \ No newline at end of file diff --git a/xodus-serialization/src/test/kotlin/ru/mipt/npm/xodus/serialization/json/EncoderDecoderTests.kt b/xodus-serialization/src/test/kotlin/ru/mipt/npm/xodus/serialization/json/EncoderDecoderTests.kt deleted file mode 100644 index ad35069..0000000 --- a/xodus-serialization/src/test/kotlin/ru/mipt/npm/xodus/serialization/json/EncoderDecoderTests.kt +++ /dev/null @@ -1,105 +0,0 @@ -package ru.mipt.npm.xodus.serialization.json - -import jetbrains.exodus.entitystore.PersistentEntityStores -import kotlinx.datetime.Instant -import kotlinx.serialization.json.* -import org.junit.jupiter.api.AfterAll -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.Test -import org.junit.jupiter.api.assertThrows -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.magix.api.MagixMessage -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.names.Name -import java.nio.file.Paths -import kotlin.test.assertEquals - -internal class EncoderDecoderTests { - companion object { - private val storePath = Paths.get(".xodus_serialization_test") - private val entityStore = PersistentEntityStores.newInstance(storePath.toString()) - - @AfterAll - @JvmStatic - fun deleteDatabase() { - entityStore.close() - storePath.toFile().deleteRecursively() - } - - @AfterEach - fun clearDatabase() { - entityStore.clear() - } - - fun checkEncodingDecodingCorrectness(json: JsonObject) { - val id = entityStore.encodeToEntity(json, "JsonObject") - assertEquals(json, entityStore.decodeFromEntity(id)) - } - - fun checkEncodingDecodingCorrectness(jsons: List) = jsons.forEach { - checkEncodingDecodingCorrectness(it) - } - - } - - @Test - fun `encoder throw Illegal exception if input is not a JsonObject`() { - assertThrows { - val json = JsonPrimitive(0) - entityStore.encodeToEntity(json, "JsonPrimitive") - } - - assertThrows { - val json = buildJsonArray {} - entityStore.encodeToEntity(json, "JsonArray") - } - } - - @Test - fun `correctly work with underlying JsonPrimitive`() { - val jsonLong = buildJsonObject { put("value", 0) } - val jsonDouble = buildJsonObject { put("value", 0.0) } - val jsonBoolean = buildJsonObject { put("value", true) } - val jsonString = buildJsonObject { put("value", "") } - - checkEncodingDecodingCorrectness(listOf(jsonLong, jsonDouble, jsonBoolean, jsonString)) - } - - @Test - fun `correctly work with underlying JsonArray`() { - checkEncodingDecodingCorrectness(buildJsonObject { putJsonArray("value") { - add(buildJsonObject { put("value", 0) }) - add(buildJsonObject { put("value", 0.0) }) - } }) - } - - @Test - fun `correctly work with underlying JsonObject`() { - checkEncodingDecodingCorrectness(buildJsonObject { - putJsonObject("value", { put("value", true) }) - }) - } - - @Test - fun testMagixMessagePropertyChangedMessage() { - val expectedMessage = MagixMessage( - "dataforge", - "dataforge", - PropertyChangedMessage( - "acceleration", - Meta { - "x" put 3.0 - "y" put 9.0 - }, - Name.parse("virtual-car"), - Name.parse("magix-virtual-car"), - time = Instant.fromEpochMilliseconds(1337) - ), - "magix-virtual-car", - user = buildJsonObject { put("name", "SCADA") } - ) - - val id = entityStore.encodeToEntity(expectedMessage, "MagixMessage") - assertEquals(expectedMessage, entityStore.decodeFromEntity>(id)) - } -} \ No newline at end of file From eb7507191eab96ddaa036668cea11a150bcde08f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 2 Jun 2022 09:21:07 +0300 Subject: [PATCH 54/74] Refactor xodus storage for local history --- .../mipt/npm/controls/storage/storageJvm.kt | 92 +++++++++---------- .../xodus/XodusDeviceMessageStorage.kt | 38 ++++---- .../src/test/kotlin/PropertyHistoryTest.kt | 43 +++++---- .../magix/storage/xodus/XodusMagixStorage.kt | 41 +++++++-- 4 files changed, 122 insertions(+), 92 deletions(-) diff --git a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt index 28135dc..3960ccd 100644 --- a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt +++ b/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt @@ -1,46 +1,46 @@ -package ru.mipt.npm.controls.storage - -import io.ktor.server.application.Application -import kotlinx.coroutines.InternalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.job -import ru.mipt.npm.magix.server.GenericMagixMessage -import space.kscience.dataforge.context.Factory -import space.kscience.dataforge.meta.Meta - -/** - * Asynchronous version of synchronous API, so for more details check relative docs - */ - -internal fun Flow.store( - client: EventStorage, - flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, -) { - filter(flowFilter).onEach { message -> - client.storeMagixMessage(message) - } -} - -/** Begin to store MagixMessages from certain flow - * @param flow flow of messages which we will store - * @param meta Meta which may have some configuration parameters for our storage and will be used in invoke method of factory - * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. - * @param flowFilter allow you to specify messages which we want to store. Always true by default. - */ -@OptIn(InternalCoroutinesApi::class) -public fun Application.store( - flow: MutableSharedFlow, - factory: Factory, - meta: Meta = Meta.EMPTY, - flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, -) { - val client = factory(meta) - - flow.store(client, flowFilter) - coroutineContext.job.invokeOnCompletion(onCancelling = true) { - client.close() - } -} +//package ru.mipt.npm.controls.storage +// +//import io.ktor.server.application.Application +//import kotlinx.coroutines.InternalCoroutinesApi +//import kotlinx.coroutines.flow.Flow +//import kotlinx.coroutines.flow.MutableSharedFlow +//import kotlinx.coroutines.flow.filter +//import kotlinx.coroutines.flow.onEach +//import kotlinx.coroutines.job +//import ru.mipt.npm.magix.server.GenericMagixMessage +//import space.kscience.dataforge.context.Factory +//import space.kscience.dataforge.meta.Meta +// +///** +// * Asynchronous version of synchronous API, so for more details check relative docs +// */ +// +//internal fun Flow.store( +// client: EventStorage, +// flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, +//) { +// filter(flowFilter).onEach { message -> +// client.storeMagixMessage(message) +// } +//} +// +///** Begin to store MagixMessages from certain flow +// * @param flow flow of messages which we will store +// * @param meta Meta which may have some configuration parameters for our storage and will be used in invoke method of factory +// * @param factory factory that will be used for creating persistent entity store instance. DefaultPersistentStoreFactory by default. +// * @param flowFilter allow you to specify messages which we want to store. Always true by default. +// */ +//@OptIn(InternalCoroutinesApi::class) +//public fun Application.store( +// flow: MutableSharedFlow, +// factory: Factory, +// meta: Meta = Meta.EMPTY, +// flowFilter: suspend (GenericMagixMessage) -> Boolean = { true }, +//) { +// val client = factory(meta) +// +// flow.store(client, flowFilter) +// coroutineContext.job.invokeOnCompletion(onCancelling = true) { +// client.close() +// } +//} diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt index ea96f36..fd2f79b 100644 --- a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt +++ b/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt @@ -27,7 +27,7 @@ import space.kscience.dataforge.names.matches import space.kscience.dataforge.names.parseAsName -internal fun StoreTransaction.writeMessage(message: DeviceMessage): Entity { +internal fun StoreTransaction.writeMessage(message: DeviceMessage): Unit { val entity: Entity = newEntity(XodusDeviceMessageStorage.DEVICE_MESSAGE_ENTITY_TYPE) val json = Json.encodeToJsonElement(DeviceMessage.serializer(), message).jsonObject val type = json["type"]?.jsonPrimitive?.content ?: error("Message json representation must have type.") @@ -43,8 +43,6 @@ internal fun StoreTransaction.writeMessage(message: DeviceMessage): Entity { entity.setProperty(DeviceMessage::targetDevice.name, it.toString()) } entity.setBlobString("json", Json.encodeToString(json)) - - return entity } @@ -65,15 +63,16 @@ public class XodusDeviceMessageStorage( ) : DeviceMessageStorage, AutoCloseable { override suspend fun write(event: DeviceMessage) { - //entityStore.encodeToEntity(event, DEVICE_MESSAGE_ENTITY_TYPE, DeviceMessage.serializer()) - entityStore.computeInTransaction { txn -> + entityStore.executeInTransaction { txn -> txn.writeMessage(event) } } - override suspend fun readAll(): List = entityStore.computeInTransaction { transaction -> - transaction.getAll( + override suspend fun readAll(): List = entityStore.computeInReadonlyTransaction { transaction -> + transaction.sort( DEVICE_MESSAGE_ENTITY_TYPE, + DeviceMessage::time.name, + true ).map { Json.decodeFromString( DeviceMessage.serializer(), @@ -87,22 +86,21 @@ public class XodusDeviceMessageStorage( range: ClosedRange?, sourceDevice: Name?, targetDevice: Name?, - ): List = entityStore.computeInTransaction { transaction -> + ): List = entityStore.computeInReadonlyTransaction { transaction -> transaction.find( DEVICE_MESSAGE_ENTITY_TYPE, "type", eventType - ).mapNotNull { - if (it.timeInRange(range) && - it.propertyMatchesName(DeviceMessage::sourceDevice.name, sourceDevice) && - it.propertyMatchesName(DeviceMessage::targetDevice.name, targetDevice) - ) { - Json.decodeFromString( - DeviceMessage.serializer(), - it.getBlobString("json") ?: error("No json content found") - ) - } else null - } + ).asSequence().filter { + it.timeInRange(range) && + it.propertyMatchesName(DeviceMessage::sourceDevice.name, sourceDevice) && + it.propertyMatchesName(DeviceMessage::targetDevice.name, targetDevice) + }.map { + Json.decodeFromString( + DeviceMessage.serializer(), + it.getBlobString("json") ?: error("No json content found") + ) + }.sortedBy { it.time }.toList() } override fun close() { @@ -110,7 +108,7 @@ public class XodusDeviceMessageStorage( } public companion object : Factory { - internal const val DEVICE_MESSAGE_ENTITY_TYPE = "DeviceMessage" + internal const val DEVICE_MESSAGE_ENTITY_TYPE = "controls-kt.message" public val XODUS_STORE_PROPERTY: Name = Name.of("xodus", "storagePath") diff --git a/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt b/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt index b1d8080..618569f 100644 --- a/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt +++ b/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt @@ -6,20 +6,19 @@ import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test -import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.storage.getPropertyHistory -import ru.mipt.npm.controls.xodus.XODUS_STORE_PROPERTY -import ru.mipt.npm.controls.xodus.XodusEventStorage -import ru.mipt.npm.xodus.serialization.json.encodeToEntity +import ru.mipt.npm.controls.xodus.XodusDeviceMessageStorage +import ru.mipt.npm.controls.xodus.query +import ru.mipt.npm.controls.xodus.writeMessage import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.Name -import java.io.File +import space.kscience.dataforge.names.asName +import java.nio.file.Files internal class PropertyHistoryTest { companion object { - private val storeName = ".property_history_test" - private val entityStore = PersistentEntityStores.newInstance(storeName) + val storeFile = Files.createTempDirectory("controls-xodus").toFile() + private val propertyChangedMessages = listOf( PropertyChangedMessage( @@ -45,28 +44,34 @@ internal class PropertyHistoryTest { @BeforeAll @JvmStatic fun createEntities() { - propertyChangedMessages.forEach { - entityStore.encodeToEntity(it, "DeviceMessage") + PersistentEntityStores.newInstance(storeFile).use { + it.executeInTransaction { transaction -> + propertyChangedMessages.forEach { message -> + transaction.writeMessage(message) + } + } } - entityStore.close() } @AfterAll @JvmStatic fun deleteDatabase() { - File(storeName).deleteRecursively() + storeFile.deleteRecursively() } } @OptIn(ExperimentalCoroutinesApi::class) @Test fun getPropertyHistoryTest() = runTest { - assertEquals( - listOf(propertyChangedMessages[0]), - getPropertyHistory( - "virtual-car", "speed", XodusEventStorage, Meta { - XODUS_STORE_PROPERTY put storeName - }) - ) + PersistentEntityStores.newInstance(storeFile).use { entityStore -> + XodusDeviceMessageStorage(entityStore).use { storage -> + assertEquals( + propertyChangedMessages[0], + storage.query( + sourceDevice = "virtual-car".asName() + ).first { it.property == "speed" } + ) + } + } } } \ No newline at end of file diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt index 007d3b1..6c76ee7 100644 --- a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt @@ -1,25 +1,52 @@ package ru.mipt.npm.magix.storage.xodus +import jetbrains.exodus.entitystore.PersistentEntityStore import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.serialization.encodeToString import kotlinx.serialization.json.JsonElement import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.api.MagixMessage import ru.mipt.npm.magix.api.MagixMessageFilter -import java.nio.file.Path public class XodusMagixStorage( - private val scope: CoroutineScope, - private val path: Path, - private val endpoint: MagixEndpoint, - private val filter: MagixMessageFilter = MagixMessageFilter(), + scope: CoroutineScope, + private val store: PersistentEntityStore, + endpoint: MagixEndpoint, + filter: MagixMessageFilter = MagixMessageFilter(), ) : AutoCloseable { - private val subscriptionJob = endpoint.subscribe(filter).onEach { - TODO() + //TODO consider message buffering + private val subscriptionJob = endpoint.subscribe(filter).onEach { message -> + store.executeInTransaction { transaction -> + transaction.newEntity(MAGIC_MESSAGE_ENTITY_TYPE).apply { + setProperty(MagixMessage<*>::origin.name, message.origin) + setProperty(MagixMessage<*>::format.name, message.format) + + setBlobString(MagixMessage<*>::payload.name, MagixEndpoint.magixJson.encodeToString(message.payload)) + + message.target?.let { + setProperty(MagixMessage<*>::target.name, it) + } + message.id?.let { + setProperty(MagixMessage<*>::id.name, it) + } + message.parentId?.let { + setProperty(MagixMessage<*>::parentId.name, it) + } + message.user?.let { + setBlobString(MagixMessage<*>::user.name, MagixEndpoint.magixJson.encodeToString(it)) + } + } + } }.launchIn(scope) override fun close() { subscriptionJob.cancel() } + + public companion object { + public const val MAGIC_MESSAGE_ENTITY_TYPE: String = "magix.message" + } } \ No newline at end of file From 07adf143cf464cde69d02219a5cf4628c43892bf Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 4 Jun 2022 15:46:13 +0300 Subject: [PATCH 55/74] Remove generic from MagixMessage --- .../client/{dfMagix.kt => controlsMagix.kt} | 33 +++++----- .../ru/mipt/npm/controls/client/tangoMagix.kt | 60 ++++++++++--------- .../npm/controls/server/deviceWebServer.kt | 4 +- .../npm/controls/demo/DemoControllerView.kt | 5 +- .../npm/controls/demo/demoDeviceServer.kt | 9 +-- .../ru/mipt/npm/magix/api/MagixEndpoint.kt | 36 +---------- .../ru/mipt/npm/magix/api/MagixFormat.kt | 47 +++++++++++++++ .../ru/mipt/npm/magix/api/MagixMessage.kt | 13 +--- .../mipt/npm/magix/api/MagixMessageFilter.kt | 8 +-- .../ru/mipt/npm/magix/api/converters.kt | 12 ++-- magix/magix-demo/src/main/kotlin/zmq.kt | 6 +- .../ru/mipt/npm/magix/client/MagixClient.java | 7 +-- .../npm/magix/client/ControlsMagixClient.kt | 11 ++-- .../npm/magix/rsocket/RSocketMagixEndpoint.kt | 23 +++---- .../ru/mipt/npm/magix/rsocket/withTcp.kt | 8 +-- .../ru/mipt/npm/magix/server/magixModule.kt | 29 ++++----- .../kotlin/ru/mipt/npm/magix/server/server.kt | 7 ++- .../npm/magix/server/zmqMagixServerSocket.kt | 9 ++- .../magix/storage/xodus/XodusMagixStorage.kt | 17 +++--- .../ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt | 22 +++---- 20 files changed, 180 insertions(+), 186 deletions(-) rename controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/{dfMagix.kt => controlsMagix.kt} (64%) create mode 100644 magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixFormat.kt diff --git a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/controlsMagix.kt similarity index 64% rename from controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt rename to controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/controlsMagix.kt index 87972c9..d5fa09d 100644 --- a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/dfMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/controlsMagix.kt @@ -9,15 +9,17 @@ import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.manager.DeviceManager import ru.mipt.npm.controls.manager.hubMessageFlow import ru.mipt.npm.controls.manager.respondHubMessage -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixMessage +import ru.mipt.npm.magix.api.* import space.kscience.dataforge.context.error import space.kscience.dataforge.context.logger -public const val DATAFORGE_MAGIX_FORMAT: String = "dataforge" +public val controlsMagixFormat: MagixFormat = MagixFormat( + DeviceMessage.serializer(), + setOf("controls-kt", "dataforge") +) -internal fun generateId(request: MagixMessage<*>): String = if (request.id != null) { +internal fun generateId(request: MagixMessage): String = if (request.id != null) { "${request.id}.response" } else { "df[${request.payload.hashCode()}" @@ -27,38 +29,31 @@ internal fun generateId(request: MagixMessage<*>): String = if (request.id != nu * Communicate with server in [Magix format](https://github.com/waltz-controls/rfc/tree/master/1) */ public fun DeviceManager.connectToMagix( - endpoint: MagixEndpoint, - endpointID: String = DATAFORGE_MAGIX_FORMAT, - preSendAction: (MagixMessage<*>) -> Unit = {} + endpoint: MagixEndpoint, + endpointID: String = controlsMagixFormat.defaultFormat, ): Job = context.launch { - endpoint.subscribe().onEach { request -> - val responsePayload = respondHubMessage(request.payload) + endpoint.subscribe(controlsMagixFormat).onEach { (request, payload) -> + val responsePayload = respondHubMessage(payload) if (responsePayload != null) { - val response = MagixMessage( + endpoint.broadcast( + format = controlsMagixFormat, origin = endpointID, payload = responsePayload, - format = DATAFORGE_MAGIX_FORMAT, id = generateId(request), parentId = request.id ) - - endpoint.broadcast(response) } }.catch { error -> logger.error(error) { "Error while responding to message" } }.launchIn(this) hubMessageFlow(this).onEach { payload -> - val magixMessage = MagixMessage( + endpoint.broadcast( + format = controlsMagixFormat, origin = endpointID, payload = payload, - format = DATAFORGE_MAGIX_FORMAT, id = "df[${payload.hashCode()}]" ) - preSendAction(magixMessage) - endpoint.broadcast( - magixMessage - ) }.catch { error -> logger.error(error) { "Error while sending a message" } }.launchIn(this) diff --git a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/tangoMagix.kt b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/tangoMagix.kt index b1d9e2d..8a88868 100644 --- a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/tangoMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/tangoMagix.kt @@ -8,8 +8,7 @@ import kotlinx.serialization.Serializable import ru.mipt.npm.controls.api.get import ru.mipt.npm.controls.api.getOrReadProperty import ru.mipt.npm.controls.manager.DeviceManager -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixMessage +import ru.mipt.npm.magix.api.* import space.kscience.dataforge.context.error import space.kscience.dataforge.context.logger import space.kscience.dataforge.meta.Meta @@ -59,33 +58,39 @@ public data class TangoPayload( val argin: Meta? = null, val argout: Meta? = null, val data: Meta? = null, - val errors: List? = null + val errors: List? = null, ) +internal val tangoMagixFormat = MagixFormat( + TangoPayload.serializer(), + setOf("tango") +) + + public fun DeviceManager.launchTangoMagix( - endpoint: MagixEndpoint, + endpoint: MagixEndpoint, endpointID: String = TANGO_MAGIX_FORMAT, ): Job { - suspend fun respond(request: MagixMessage, payloadBuilder: (TangoPayload) -> TangoPayload) { + + suspend fun respond(request: MagixMessage, payload: TangoPayload, payloadBuilder: (TangoPayload) -> TangoPayload) { endpoint.broadcast( - request.copy( - id = generateId(request), - parentId = request.id, - origin = endpointID, - payload = payloadBuilder(request.payload) - ) + tangoMagixFormat, + id = generateId(request), + parentId = request.id, + origin = endpointID, + payload = payloadBuilder(payload) ) } return context.launch { - endpoint.subscribe().onEach { request -> + endpoint.subscribe(tangoMagixFormat).onEach { (request, payload) -> try { - val device = get(request.payload.device) - when (request.payload.action) { + val device = get(payload.device) + when (payload.action) { TangoAction.read -> { - val value = device.getOrReadProperty(request.payload.name) - respond(request) { requestPayload -> + val value = device.getOrReadProperty(payload.name) + respond(request, payload) { requestPayload -> requestPayload.copy( value = value, quality = TangoQuality.VALID @@ -93,12 +98,12 @@ public fun DeviceManager.launchTangoMagix( } } TangoAction.write -> { - request.payload.value?.let { value -> - device.writeProperty(request.payload.name, value) + payload.value?.let { value -> + device.writeProperty(payload.name, value) } //wait for value to be written and return final state - val value = device.getOrReadProperty(request.payload.name) - respond(request) { requestPayload -> + val value = device.getOrReadProperty(payload.name) + respond(request, payload) { requestPayload -> requestPayload.copy( value = value, quality = TangoQuality.VALID @@ -106,8 +111,8 @@ public fun DeviceManager.launchTangoMagix( } } TangoAction.exec -> { - val result = device.execute(request.payload.name, request.payload.argin) - respond(request) { requestPayload -> + val result = device.execute(payload.name, payload.argin) + respond(request, payload) { requestPayload -> requestPayload.copy( argout = result, quality = TangoQuality.VALID @@ -119,12 +124,11 @@ public fun DeviceManager.launchTangoMagix( } catch (ex: Exception) { logger.error(ex) { "Error while responding to message" } endpoint.broadcast( - request.copy( - id = generateId(request), - parentId = request.id, - origin = endpointID, - payload = request.payload.copy(quality = TangoQuality.WARNING) - ) + tangoMagixFormat, + id = generateId(request), + parentId = request.id, + origin = endpointID, + payload = payload.copy(quality = TangoQuality.WARNING) ) } }.launchIn(this) diff --git a/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt b/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt index ca1b738..a2c67dd 100644 --- a/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt +++ b/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt @@ -33,7 +33,7 @@ import ru.mipt.npm.controls.api.getOrNull import ru.mipt.npm.controls.manager.DeviceManager import ru.mipt.npm.controls.manager.respondHubMessage import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.server.GenericMagixMessage +import ru.mipt.npm.magix.api.MagixMessage import ru.mipt.npm.magix.server.launchMagixServerRawRSocket import ru.mipt.npm.magix.server.magixModule import space.kscience.dataforge.meta.toMeta @@ -212,7 +212,7 @@ public fun Application.deviceManagerModule( } } - val magixFlow = MutableSharedFlow( + val magixFlow = MutableSharedFlow( buffer, extraBufferCapacity = buffer ) diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt index 64e0fa0..ba6ff8e 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt @@ -8,7 +8,6 @@ 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.demo.DemoDevice.Companion.cosScale import ru.mipt.npm.controls.demo.DemoDevice.Companion.sinScale @@ -52,9 +51,9 @@ class DemoController : Controller(), ContextAware { //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()) + val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost") deviceManager.connectToMagix(deviceEndpoint) - val visualEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost", DeviceMessage.serializer()) + val visualEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost") visualizer = visualEndpoint.startDemoDeviceServer() opcUaServer.startup() diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt index 7f65add..9cf7589 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt @@ -11,9 +11,10 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.html.div import kotlinx.html.link -import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.client.controlsMagixFormat import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.api.subscribe import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.double import space.kscience.plotly.layout @@ -54,7 +55,7 @@ suspend fun Trace.updateXYFrom(flow: Flow>>) { } -suspend fun MagixEndpoint.startDemoDeviceServer(): ApplicationEngine = embeddedServer(CIO, 9090) { +suspend fun MagixEndpoint.startDemoDeviceServer(): ApplicationEngine = embeddedServer(CIO, 9090) { install(WebSockets) install(RSocketSupport) @@ -66,8 +67,8 @@ suspend fun MagixEndpoint.startDemoDeviceServer(): ApplicationEng val cosFlow = MutableSharedFlow()// = device.cos.flow() launch { - subscribe().collect { magix -> - (magix.payload as? PropertyChangedMessage)?.let { message -> + subscribe(controlsMagixFormat).collect { (magix, payload) -> + (payload as? PropertyChangedMessage)?.let { message -> when (message.property) { "sin" -> sinFlow.emit(message.value) "cos" -> cosFlow.emit(message.value) diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixEndpoint.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixEndpoint.kt index 78ab54d..c531583 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixEndpoint.kt +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixEndpoint.kt @@ -1,29 +1,26 @@ package ru.mipt.npm.magix.api import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonElement /** * Inwards API of magix endpoint used to build services */ -public interface MagixEndpoint { +public interface MagixEndpoint { /** * Subscribe to a [Flow] of messages */ public fun subscribe( filter: MagixMessageFilter = MagixMessageFilter.ALL, - ): Flow> + ): Flow /** * Send an event */ public suspend fun broadcast( - message: MagixMessage, + message: MagixMessage, ) public companion object { @@ -53,31 +50,4 @@ public interface MagixEndpoint { encodeDefaults = false } } -} - -/** - * Specialize this raw json endpoint to use specific serializer - */ -public fun MagixEndpoint.specialize( - payloadSerializer: KSerializer -): MagixEndpoint = object : MagixEndpoint { - override fun subscribe( - filter: MagixMessageFilter - ): Flow> = this@specialize.subscribe(filter).map { message -> - message.replacePayload { payload -> - MagixEndpoint.magixJson.decodeFromJsonElement(payloadSerializer, payload) - } - } - - override suspend fun broadcast(message: MagixMessage) { - this@specialize.broadcast( - message.replacePayload { payload -> - MagixEndpoint.magixJson.encodeToJsonElement( - payloadSerializer, - payload - ) - } - ) - } - } \ No newline at end of file diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixFormat.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixFormat.kt new file mode 100644 index 0000000..d2aa466 --- /dev/null +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixFormat.kt @@ -0,0 +1,47 @@ +package ru.mipt.npm.magix.api + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.serialization.KSerializer +import kotlinx.serialization.json.JsonElement +import ru.mipt.npm.magix.api.MagixEndpoint.Companion.magixJson + +public data class MagixFormat( + val serializer: KSerializer, + val formats: Set, +) { + val defaultFormat: String get() = formats.firstOrNull() ?: "magix" +} + +public fun MagixEndpoint.subscribe( + format: MagixFormat, + originFilter: Collection? = null, + targetFilter: Collection? = null, +): Flow> = subscribe( + MagixMessageFilter(format = format.formats, origin = originFilter, target = targetFilter) +).map { + val value: T = magixJson.decodeFromJsonElement(format.serializer, it.payload) + it to value +} + +public suspend fun MagixEndpoint.broadcast( + format: MagixFormat, + payload: T, + target: String? = null, + id: String? = null, + parentId: String? = null, + user: JsonElement? = null, + origin: String = format.defaultFormat, +) { + val message = MagixMessage( + origin = origin, + payload = magixJson.encodeToJsonElement(format.serializer, payload), + format = format.defaultFormat, + target = target, + id = id, + parentId = parentId, + user = user + ) + broadcast(message) +} + diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt index e804a69..e593434 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt @@ -24,19 +24,12 @@ import kotlinx.serialization.json.JsonElement * */ @Serializable -public data class MagixMessage( +public data class MagixMessage( val origin: String, - val payload: T, + val payload: JsonElement, val format: String = origin, val target: String? = null, val id: String? = null, val parentId: String? = null, val user: JsonElement? = null, -) - -/** - * Create message with same field but replaced payload - */ -@Suppress("UNCHECKED_CAST") -public fun MagixMessage.replacePayload(payloadTransform: (T) -> R): MagixMessage = - MagixMessage(origin, payloadTransform(payload), format, target, id, parentId, user) \ No newline at end of file +) \ No newline at end of file diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessageFilter.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessageFilter.kt index 6083ac4..fb6ab4e 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessageFilter.kt +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessageFilter.kt @@ -6,9 +6,9 @@ import kotlinx.serialization.Serializable @Serializable public data class MagixMessageFilter( - val format: List? = null, - val origin: List? = null, - val target: List? = null, + val format: Collection? = null, + val origin: Collection? = null, + val target: Collection? = null, ) { public companion object { public val ALL: MagixMessageFilter = MagixMessageFilter() @@ -18,7 +18,7 @@ public data class MagixMessageFilter( /** * Filter a [Flow] of messages based on given filter */ -public fun Flow>.filter(filter: MagixMessageFilter): Flow> { +public fun Flow.filter(filter: MagixMessageFilter): Flow { if (filter == MagixMessageFilter.ALL) { return this } diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt index 1ab2de7..dbdea23 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt @@ -4,20 +4,20 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.serialization.json.JsonElement /** * Launch magix message converter service */ public fun CoroutineScope.launchMagixConverter( - inputEndpoint: MagixEndpoint, - outputEndpoint: MagixEndpoint, + endpoint: MagixEndpoint, filter: MagixMessageFilter, outputFormat: String, newOrigin: String? = null, - transformer: suspend (T) -> R, -): Job = inputEndpoint.subscribe(filter).onEach { message-> + transformer: suspend (JsonElement) -> JsonElement, +): Job = endpoint.subscribe(filter).onEach { message-> val newPayload = transformer(message.payload) - val transformed: MagixMessage = MagixMessage( + val transformed: MagixMessage = MagixMessage( newOrigin ?: message.origin, newPayload, outputFormat, @@ -26,5 +26,5 @@ public fun CoroutineScope.launchMagixConverter( message.parentId, message.user ) - outputEndpoint.broadcast(transformed) + endpoint.broadcast(transformed) }.launchIn(this) diff --git a/magix/magix-demo/src/main/kotlin/zmq.kt b/magix/magix-demo/src/main/kotlin/zmq.kt index 44bafc9..868b503 100644 --- a/magix/magix-demo/src/main/kotlin/zmq.kt +++ b/magix/magix-demo/src/main/kotlin/zmq.kt @@ -15,7 +15,7 @@ import java.awt.Desktop import java.net.URI -suspend fun MagixEndpoint.sendJson( +suspend fun MagixEndpoint.sendJson( origin: String, format: String = "json", target: String? = null, @@ -44,11 +44,11 @@ suspend fun main(): Unit = coroutineScope { logger.info("Starting client") //Create zmq magix endpoint and wait for to finish - ZmqMagixEndpoint("tcp://localhost", JsonObject.serializer()).use { client -> + ZmqMagixEndpoint("tcp://localhost").use { client -> logger.info("Starting subscription") client.subscribe().onEach { println(it.payload) - if (it.payload["index"]?.jsonPrimitive?.int == numberOfMessages) { + if (it.payload.jsonObject["index"]?.jsonPrimitive?.int == numberOfMessages) { logger.info("Index $numberOfMessages reached. Terminating") cancel() } diff --git a/magix/magix-java-client/src/main/java/ru/mipt/npm/magix/client/MagixClient.java b/magix/magix-java-client/src/main/java/ru/mipt/npm/magix/client/MagixClient.java index 50d6f09..58af6d2 100644 --- a/magix/magix-java-client/src/main/java/ru/mipt/npm/magix/client/MagixClient.java +++ b/magix/magix-java-client/src/main/java/ru/mipt/npm/magix/client/MagixClient.java @@ -12,9 +12,9 @@ import java.util.concurrent.Flow; * @param */ public interface MagixClient { - void broadcast(MagixMessage msg) throws IOException; + void broadcast(MagixMessage msg) throws IOException; - Flow.Publisher> subscribe(); + Flow.Publisher subscribe(); /** * Create a magix endpoint client using RSocket with raw tcp connection @@ -23,7 +23,7 @@ public interface MagixClient { * @return the client */ static MagixClient rSocketTcp(String host, int port) { - return ControlsMagixClient.Companion.rSocketTcp(host, port, JsonElement.Companion.serializer()); + return ControlsMagixClient.Companion.rSocketTcp(host, port); } /** @@ -31,7 +31,6 @@ public interface MagixClient { * @param host host name of magix server event loop * @param port port of magix server event loop * @param path - * @return */ static MagixClient rSocketWs(String host, int port, String path) { return ControlsMagixClient.Companion.rSocketWs(host, port, JsonElement.Companion.serializer(), path); diff --git a/magix/magix-java-client/src/main/kotlin/ru/mipt/npm/magix/client/ControlsMagixClient.kt b/magix/magix-java-client/src/main/kotlin/ru/mipt/npm/magix/client/ControlsMagixClient.kt index 476a73d..11c2681 100644 --- a/magix/magix-java-client/src/main/kotlin/ru/mipt/npm/magix/client/ControlsMagixClient.kt +++ b/magix/magix-java-client/src/main/kotlin/ru/mipt/npm/magix/client/ControlsMagixClient.kt @@ -11,25 +11,24 @@ import ru.mipt.npm.magix.rsocket.rSocketWithWebSockets import java.util.concurrent.Flow internal class ControlsMagixClient( - private val endpoint: MagixEndpoint, + private val endpoint: MagixEndpoint, private val filter: MagixMessageFilter, ) : MagixClient { - override fun broadcast(msg: MagixMessage): Unit = runBlocking { + override fun broadcast(msg: MagixMessage): Unit = runBlocking { endpoint.broadcast(msg) } - override fun subscribe(): Flow.Publisher> = endpoint.subscribe(filter).asPublisher() + override fun subscribe(): Flow.Publisher = endpoint.subscribe(filter).asPublisher() companion object { fun rSocketTcp( host: String, port: Int, - payloadSerializer: KSerializer ): ControlsMagixClient { val endpoint = runBlocking { - MagixEndpoint.rSocketWithTcp(host, payloadSerializer, port) + MagixEndpoint.rSocketWithTcp(host, port) } return ControlsMagixClient(endpoint, MagixMessageFilter()) } @@ -41,7 +40,7 @@ internal class ControlsMagixClient( path: String = "/rsocket" ): ControlsMagixClient { val endpoint = runBlocking { - MagixEndpoint.rSocketWithWebSockets(host, payloadSerializer, port, path) + MagixEndpoint.rSocketWithWebSockets(host, port, path) } return ControlsMagixClient(endpoint, MagixMessageFilter()) } diff --git a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt index 7fdcee4..4b05e6f 100644 --- a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt +++ b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt @@ -16,7 +16,6 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext -import kotlinx.serialization.KSerializer import kotlinx.serialization.encodeToString import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixMessage @@ -25,27 +24,24 @@ import ru.mipt.npm.magix.api.filter import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext -public class RSocketMagixEndpoint( - payloadSerializer: KSerializer, +public class RSocketMagixEndpoint( private val rSocket: RSocket, private val coroutineContext: CoroutineContext, -) : MagixEndpoint { - - private val serializer = MagixMessage.serializer(payloadSerializer) +) : MagixEndpoint { override fun subscribe( filter: MagixMessageFilter, - ): Flow> { + ): Flow { val payload = buildPayload { data(MagixEndpoint.magixJson.encodeToString(filter)) } val flow = rSocket.requestStream(payload) return flow.map { - MagixEndpoint.magixJson.decodeFromString(serializer, it.data.readText()) + MagixEndpoint.magixJson.decodeFromString(MagixMessage.serializer(), it.data.readText()) }.filter(filter).flowOn(coroutineContext[CoroutineDispatcher] ?: Dispatchers.Unconfined) } - override suspend fun broadcast(message: MagixMessage) { + override suspend fun broadcast(message: MagixMessage) { withContext(coroutineContext) { - val payload = buildPayload { data(MagixEndpoint.magixJson.encodeToString(serializer, message)) } + val payload = buildPayload { data(MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message)) } rSocket.fireAndForget(payload) } } @@ -63,13 +59,12 @@ internal fun buildConnector(rSocketConfig: RSocketConnectorBuilder.ConnectionCon /** * Build a websocket based endpoint connected to [host], [port] and given routing [path] */ -public suspend fun MagixEndpoint.Companion.rSocketWithWebSockets( +public suspend fun MagixEndpoint.Companion.rSocketWithWebSockets( host: String, - payloadSerializer: KSerializer, port: Int = DEFAULT_MAGIX_HTTP_PORT, path: String = "/rsocket", rSocketConfig: RSocketConnectorBuilder.ConnectionConfigBuilder.() -> Unit = {}, -): RSocketMagixEndpoint { +): RSocketMagixEndpoint { val client = HttpClient { install(WebSockets) install(RSocketSupport) { @@ -84,5 +79,5 @@ public suspend fun MagixEndpoint.Companion.rSocketWithWebSockets( client.close() } - return RSocketMagixEndpoint(payloadSerializer, rSocket, coroutineContext) + return RSocketMagixEndpoint(rSocket, coroutineContext) } \ No newline at end of file diff --git a/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt b/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt index d3c0719..05a596b 100644 --- a/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt +++ b/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt @@ -4,7 +4,6 @@ import io.ktor.network.sockets.SocketOptions import io.ktor.util.InternalAPI import io.rsocket.kotlin.core.RSocketConnectorBuilder import io.rsocket.kotlin.transport.ktor.tcp.TcpClientTransport -import kotlinx.serialization.KSerializer import ru.mipt.npm.magix.api.MagixEndpoint import kotlin.coroutines.coroutineContext @@ -13,13 +12,12 @@ import kotlin.coroutines.coroutineContext * Create a plain TCP based [RSocketMagixEndpoint] connected to [host] and [port] */ @OptIn(InternalAPI::class) -public suspend fun MagixEndpoint.Companion.rSocketWithTcp( +public suspend fun MagixEndpoint.Companion.rSocketWithTcp( host: String, - payloadSerializer: KSerializer, port: Int = DEFAULT_MAGIX_RAW_PORT, tcpConfig: SocketOptions.TCPClientSocketOptions.() -> Unit = {}, rSocketConfig: RSocketConnectorBuilder.ConnectionConfigBuilder.() -> Unit = {}, -): RSocketMagixEndpoint { +): RSocketMagixEndpoint { val transport = TcpClientTransport( hostname = host, port = port, @@ -27,5 +25,5 @@ public suspend fun MagixEndpoint.Companion.rSocketWithTcp( ) val rSocket = buildConnector(rSocketConfig).connect(transport) - return RSocketMagixEndpoint(payloadSerializer, rSocket, coroutineContext) + return RSocketMagixEndpoint(rSocket, coroutineContext) } diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt index a19c8a4..70d683f 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt +++ b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt @@ -21,44 +21,39 @@ import io.rsocket.kotlin.payload.data import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* import kotlinx.html.* -import kotlinx.serialization.KSerializer -import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString import ru.mipt.npm.magix.api.MagixEndpoint.Companion.magixJson import ru.mipt.npm.magix.api.MagixMessage import ru.mipt.npm.magix.api.MagixMessageFilter import ru.mipt.npm.magix.api.filter import java.util.* -public typealias GenericMagixMessage = MagixMessage -internal val genericMessageSerializer: KSerializer> = - MagixMessage.serializer(JsonElement.serializer()) - - -internal fun CoroutineScope.magixAcceptor(magixFlow: MutableSharedFlow) = ConnectionAcceptor { +internal fun CoroutineScope.magixAcceptor(magixFlow: MutableSharedFlow) = ConnectionAcceptor { RSocketRequestHandler { //handler for request/stream requestStream { request: Payload -> val filter = magixJson.decodeFromString(MagixMessageFilter.serializer(), request.data.readText()) magixFlow.filter(filter).map { message -> - val string = magixJson.encodeToString(genericMessageSerializer, message) + val string = magixJson.encodeToString(message) buildPayload { data(string) } } } fireAndForget { request: Payload -> - val message = magixJson.decodeFromString(genericMessageSerializer, request.data.readText()) + val message = magixJson.decodeFromString(request.data.readText()) magixFlow.emit(message) } // bi-directional connection requestChannel { request: Payload, input: Flow -> input.onEach { - magixFlow.emit(magixJson.decodeFromString(genericMessageSerializer, it.data.readText())) + magixFlow.emit(magixJson.decodeFromString(it.data.readText())) }.launchIn(this@magixAcceptor) val filter = magixJson.decodeFromString(MagixMessageFilter.serializer(), request.data.readText()) magixFlow.filter(filter).map { message -> - val string = magixJson.encodeToString(genericMessageSerializer, message) + val string = magixJson.encodeToString(message) buildPayload { data(string) } } } @@ -86,7 +81,7 @@ private fun ApplicationCall.buildFilter(): MagixMessageFilter { /** * Attache magix http/sse and websocket-based rsocket event loop + statistics page to existing [MutableSharedFlow] */ -public fun Application.magixModule(magixFlow: MutableSharedFlow, route: String = "/") { +public fun Application.magixModule(magixFlow: MutableSharedFlow, route: String = "/") { if (pluginOrNull(WebSockets) == null) { install(WebSockets) } @@ -126,7 +121,7 @@ public fun Application.magixModule(magixFlow: MutableSharedFlow li { code { - +magixJson.encodeToString(genericMessageSerializer, message) + +magixJson.encodeToString(message) } } } @@ -138,14 +133,14 @@ public fun Application.magixModule(magixFlow: MutableSharedFlow() + val message = call.receive() magixFlow.emit(message) } //rSocket server. Filter from Payload @@ -158,6 +153,6 @@ public fun Application.magixModule(magixFlow: MutableSharedFlow(buffer) + val magixFlow = MutableSharedFlow(buffer) magixModule(magixFlow, route) } \ No newline at end of file diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt index 2a59c11..bc84f63 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt +++ b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt @@ -14,12 +14,13 @@ import org.slf4j.LoggerFactory import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_HTTP_PORT import ru.mipt.npm.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_RAW_PORT +import ru.mipt.npm.magix.api.MagixMessage /** * Raw TCP magix server */ public fun CoroutineScope.launchMagixServerRawRSocket( - magixFlow: MutableSharedFlow, + magixFlow: MutableSharedFlow, rawSocketPort: Int = DEFAULT_MAGIX_RAW_PORT, ): TcpServer { val tcpTransport = TcpServerTransport(port = rawSocketPort) @@ -41,10 +42,10 @@ public fun CoroutineScope.startMagixServer( buffer: Int = 100, enableRawRSocket: Boolean = true, enableZmq: Boolean = true, - applicationConfiguration: Application.(MutableSharedFlow) -> Unit = {}, + applicationConfiguration: Application.(MutableSharedFlow) -> Unit = {}, ): ApplicationEngine { val logger = LoggerFactory.getLogger("magix-server") - val magixFlow = MutableSharedFlow( + val magixFlow = MutableSharedFlow( buffer, extraBufferCapacity = buffer ) diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/zmqMagixServerSocket.kt b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/zmqMagixServerSocket.kt index e62acd7..f0c62b8 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/zmqMagixServerSocket.kt +++ b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/zmqMagixServerSocket.kt @@ -4,13 +4,16 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString import org.slf4j.LoggerFactory import org.zeromq.SocketType import org.zeromq.ZContext import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.api.MagixMessage public fun CoroutineScope.launchMagixServerZmqSocket( - magixFlow: MutableSharedFlow, + magixFlow: MutableSharedFlow, localHost: String = "tcp://*", zmqPubSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT, zmqPullSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, @@ -22,7 +25,7 @@ public fun CoroutineScope.launchMagixServerZmqSocket( val pubSocket = context.createSocket(SocketType.PUB) pubSocket.bind("$localHost:$zmqPubSocketPort") magixFlow.onEach { message -> - val string = MagixEndpoint.magixJson.encodeToString(genericMessageSerializer, message) + val string = MagixEndpoint.magixJson.encodeToString(message) pubSocket.send(string) logger.debug("Published: $string") }.launchIn(this) @@ -36,7 +39,7 @@ public fun CoroutineScope.launchMagixServerZmqSocket( val string: String? = pullSocket.recvStr() if (string != null) { logger.debug("Received: $string") - val message = MagixEndpoint.magixJson.decodeFromString(genericMessageSerializer, string) + val message = MagixEndpoint.magixJson.decodeFromString(string) magixFlow.emit(message) } } diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt index 6c76ee7..3b98368 100644 --- a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.JsonElement import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixMessage import ru.mipt.npm.magix.api.MagixMessageFilter @@ -13,7 +12,7 @@ import ru.mipt.npm.magix.api.MagixMessageFilter public class XodusMagixStorage( scope: CoroutineScope, private val store: PersistentEntityStore, - endpoint: MagixEndpoint, + endpoint: MagixEndpoint, filter: MagixMessageFilter = MagixMessageFilter(), ) : AutoCloseable { @@ -21,22 +20,22 @@ public class XodusMagixStorage( private val subscriptionJob = endpoint.subscribe(filter).onEach { message -> store.executeInTransaction { transaction -> transaction.newEntity(MAGIC_MESSAGE_ENTITY_TYPE).apply { - setProperty(MagixMessage<*>::origin.name, message.origin) - setProperty(MagixMessage<*>::format.name, message.format) + setProperty(MagixMessage::origin.name, message.origin) + setProperty(MagixMessage::format.name, message.format) - setBlobString(MagixMessage<*>::payload.name, MagixEndpoint.magixJson.encodeToString(message.payload)) + setBlobString(MagixMessage::payload.name, MagixEndpoint.magixJson.encodeToString(message.payload)) message.target?.let { - setProperty(MagixMessage<*>::target.name, it) + setProperty(MagixMessage::target.name, it) } message.id?.let { - setProperty(MagixMessage<*>::id.name, it) + setProperty(MagixMessage::id.name, it) } message.parentId?.let { - setProperty(MagixMessage<*>::parentId.name, it) + setProperty(MagixMessage::parentId.name, it) } message.user?.let { - setBlobString(MagixMessage<*>::user.name, MagixEndpoint.magixJson.encodeToString(it)) + setBlobString(MagixMessage::user.name, MagixEndpoint.magixJson.encodeToString(it)) } } } diff --git a/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt b/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt index 99f209e..56846e9 100644 --- a/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt +++ b/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt @@ -4,7 +4,8 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.flowOn -import kotlinx.serialization.KSerializer +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString import org.zeromq.SocketType import org.zeromq.ZContext import org.zeromq.ZMQ @@ -16,19 +17,16 @@ import ru.mipt.npm.magix.api.filter import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext -public class ZmqMagixEndpoint( +public class ZmqMagixEndpoint( private val host: String, - payloadSerializer: KSerializer, private val pubPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT, private val pullPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, private val coroutineContext: CoroutineContext = Dispatchers.IO, -) : MagixEndpoint, AutoCloseable { +) : MagixEndpoint, AutoCloseable { private val zmqContext by lazy { ZContext() } - private val serializer = MagixMessage.serializer(payloadSerializer) - @OptIn(ExperimentalCoroutinesApi::class) - override fun subscribe(filter: MagixMessageFilter): Flow> { + override fun subscribe(filter: MagixMessageFilter): Flow { val socket = zmqContext.createSocket(SocketType.SUB) socket.connect("$host:$pubPort") socket.subscribe("") @@ -42,7 +40,7 @@ public class ZmqMagixEndpoint( //This is a blocking call. val string: String? = socket.recvStr() if (string != null) { - val message = MagixEndpoint.magixJson.decodeFromString(serializer, string) + val message = MagixEndpoint.magixJson.decodeFromString(string) send(message) } } catch (t: Throwable) { @@ -64,8 +62,8 @@ public class ZmqMagixEndpoint( } } - override suspend fun broadcast(message: MagixMessage): Unit = withContext(coroutineContext) { - val string = MagixEndpoint.magixJson.encodeToString(serializer, message) + override suspend fun broadcast(message: MagixMessage): Unit = withContext(coroutineContext) { + val string = MagixEndpoint.magixJson.encodeToString(message) publishSocket.send(string) } @@ -76,12 +74,10 @@ public class ZmqMagixEndpoint( public suspend fun MagixEndpoint.Companion.zmq( host: String, - payloadSerializer: KSerializer, pubPort: Int = DEFAULT_MAGIX_ZMQ_PUB_PORT, pullPort: Int = DEFAULT_MAGIX_ZMQ_PULL_PORT, -): ZmqMagixEndpoint = ZmqMagixEndpoint( +): ZmqMagixEndpoint = ZmqMagixEndpoint( host, - payloadSerializer, pubPort, pullPort, coroutineContext = coroutineContext From 879073e3391f92909b6b634872d23be7e7f677b9 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 5 Jun 2022 10:28:46 +0300 Subject: [PATCH 56/74] Add native rsocket-tcp --- controls-core/build.gradle.kts | 1 + .../npm/controls/demo/demoDeviceServer.kt | 2 +- .../ru/mipt/npm/magix/client/MagixClient.java | 4 +-- .../npm/magix/client/ControlsMagixClient.kt | 2 -- magix/magix-rsocket/build.gradle.kts | 10 +++++-- .../ru/mipt/npm/magix/rsocket/withTcp.kt | 2 -- .../linuxX64Main/kotlin/rsocket/withTcp.kt | 29 +++++++++++++++++++ magix/magix-server/build.gradle.kts | 2 +- 8 files changed, 41 insertions(+), 11 deletions(-) create mode 100644 magix/magix-rsocket/src/linuxX64Main/kotlin/rsocket/withTcp.kt diff --git a/controls-core/build.gradle.kts b/controls-core/build.gradle.kts index 2510957..777cb1e 100644 --- a/controls-core/build.gradle.kts +++ b/controls-core/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("ru.mipt.npm.gradle.mpp") + id("ru.mipt.npm.gradle.native") `maven-publish` } diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt index 9cf7589..0794f03 100644 --- a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt +++ b/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt @@ -67,7 +67,7 @@ suspend fun MagixEndpoint.startDemoDeviceServer(): ApplicationEngine = embeddedS val cosFlow = MutableSharedFlow()// = device.cos.flow() launch { - subscribe(controlsMagixFormat).collect { (magix, payload) -> + subscribe(controlsMagixFormat).collect { (_, payload) -> (payload as? PropertyChangedMessage)?.let { message -> when (message.property) { "sin" -> sinFlow.emit(message.value) diff --git a/magix/magix-java-client/src/main/java/ru/mipt/npm/magix/client/MagixClient.java b/magix/magix-java-client/src/main/java/ru/mipt/npm/magix/client/MagixClient.java index 58af6d2..3da2364 100644 --- a/magix/magix-java-client/src/main/java/ru/mipt/npm/magix/client/MagixClient.java +++ b/magix/magix-java-client/src/main/java/ru/mipt/npm/magix/client/MagixClient.java @@ -30,9 +30,9 @@ public interface MagixClient { * * @param host host name of magix server event loop * @param port port of magix server event loop - * @param path + * @param path context path for WS connection */ static MagixClient rSocketWs(String host, int port, String path) { - return ControlsMagixClient.Companion.rSocketWs(host, port, JsonElement.Companion.serializer(), path); + return ControlsMagixClient.Companion.rSocketWs(host, port, path); } } diff --git a/magix/magix-java-client/src/main/kotlin/ru/mipt/npm/magix/client/ControlsMagixClient.kt b/magix/magix-java-client/src/main/kotlin/ru/mipt/npm/magix/client/ControlsMagixClient.kt index 11c2681..4abb15f 100644 --- a/magix/magix-java-client/src/main/kotlin/ru/mipt/npm/magix/client/ControlsMagixClient.kt +++ b/magix/magix-java-client/src/main/kotlin/ru/mipt/npm/magix/client/ControlsMagixClient.kt @@ -2,7 +2,6 @@ package ru.mipt.npm.magix.client import kotlinx.coroutines.jdk9.asPublisher import kotlinx.coroutines.runBlocking -import kotlinx.serialization.KSerializer import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixMessage import ru.mipt.npm.magix.api.MagixMessageFilter @@ -36,7 +35,6 @@ internal class ControlsMagixClient( fun rSocketWs( host: String, port: Int, - payloadSerializer: KSerializer, path: String = "/rsocket" ): ControlsMagixClient { val endpoint = runBlocking { diff --git a/magix/magix-rsocket/build.gradle.kts b/magix/magix-rsocket/build.gradle.kts index 3d445a0..b5c1eb8 100644 --- a/magix/magix-rsocket/build.gradle.kts +++ b/magix/magix-rsocket/build.gradle.kts @@ -26,9 +26,13 @@ kotlin { implementation("io.rsocket.kotlin:rsocket-ktor-client:$rsocketVersion") } } - jvmMain{ - dependencies{ - implementation("io.ktor:ktor-network:$ktorVersion") + jvmMain { + dependencies { + implementation("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") + } + } + linuxX64Main{ + dependencies { implementation("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") } } diff --git a/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt b/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt index 05a596b..b42ee4f 100644 --- a/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt +++ b/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt @@ -1,7 +1,6 @@ package ru.mipt.npm.magix.rsocket import io.ktor.network.sockets.SocketOptions -import io.ktor.util.InternalAPI import io.rsocket.kotlin.core.RSocketConnectorBuilder import io.rsocket.kotlin.transport.ktor.tcp.TcpClientTransport import ru.mipt.npm.magix.api.MagixEndpoint @@ -11,7 +10,6 @@ import kotlin.coroutines.coroutineContext /** * Create a plain TCP based [RSocketMagixEndpoint] connected to [host] and [port] */ -@OptIn(InternalAPI::class) public suspend fun MagixEndpoint.Companion.rSocketWithTcp( host: String, port: Int = DEFAULT_MAGIX_RAW_PORT, diff --git a/magix/magix-rsocket/src/linuxX64Main/kotlin/rsocket/withTcp.kt b/magix/magix-rsocket/src/linuxX64Main/kotlin/rsocket/withTcp.kt new file mode 100644 index 0000000..8e1d596 --- /dev/null +++ b/magix/magix-rsocket/src/linuxX64Main/kotlin/rsocket/withTcp.kt @@ -0,0 +1,29 @@ +package rsocket + +import io.ktor.network.sockets.SocketOptions +import io.rsocket.kotlin.core.RSocketConnectorBuilder +import io.rsocket.kotlin.transport.ktor.tcp.TcpClientTransport +import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.rsocket.RSocketMagixEndpoint +import ru.mipt.npm.magix.rsocket.buildConnector +import kotlin.coroutines.coroutineContext + + +/** + * Create a plain TCP based [RSocketMagixEndpoint] connected to [host] and [port] + */ +public suspend fun MagixEndpoint.Companion.rSocketWithTcp( + host: String, + port: Int = DEFAULT_MAGIX_RAW_PORT, + tcpConfig: SocketOptions.TCPClientSocketOptions.() -> Unit = {}, + rSocketConfig: RSocketConnectorBuilder.ConnectionConfigBuilder.() -> Unit = {}, +): RSocketMagixEndpoint { + val transport = TcpClientTransport( + hostname = host, + port = port, + configure = tcpConfig + ) + val rSocket = buildConnector(rSocketConfig).connect(transport) + + return RSocketMagixEndpoint(rSocket, coroutineContext) +} diff --git a/magix/magix-server/build.gradle.kts b/magix/magix-server/build.gradle.kts index 5215f19..f73528d 100644 --- a/magix/magix-server/build.gradle.kts +++ b/magix/magix-server/build.gradle.kts @@ -19,7 +19,7 @@ val rsocketVersion: String by rootProject.extra val ktorVersion: String = ru.mipt.npm.gradle.KScienceVersions.ktorVersion dependencies{ - api(project(":magix:magix-api")) + api(projects.magix.magixApi) api("io.ktor:ktor-server-cio:$ktorVersion") api("io.ktor:ktor-server-websockets:$ktorVersion") api("io.ktor:ktor-server-content-negotiation:$ktorVersion") From 025a444db8a1463c71761f854ca23e2d4ab55fa8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 5 Jun 2022 12:26:34 +0300 Subject: [PATCH 57/74] Fix demos and project structure --- .../controls-xodus}/build.gradle.kts | 0 .../xodus/XodusDeviceMessageStorage.kt | 0 .../src/test/kotlin/PropertyHistoryTest.kt | 0 demo/{ => all-things}/build.gradle.kts | 0 .../npm/controls/demo/DemoControllerView.kt | 0 .../ru/mipt/npm/controls/demo/DemoDevice.kt | 0 .../npm/controls/demo/demoDeviceServer.kt | 0 .../controls/demo/generateMessageSchema.kt | 0 demo/car/build.gradle.kts | 10 +-- .../npm/controls/demo/car/MagixVirtualCar.kt | 22 +++---- .../controls/demo/car/VirtualCarController.kt | 50 ++++++++------- .../ru/mipt/npm/magix/api/MagixFormat.kt | 4 +- .../ru/mipt/npm/magix/api/MagixMessage.kt | 4 +- .../ru/mipt/npm/magix/api/converters.kt | 4 +- magix/magix-demo/src/main/kotlin/zmq.kt | 2 +- .../kotlin/ru/mipt/npm/magix/server/server.kt | 2 +- .../magix/storage/xodus/XodusMagixStorage.kt | 62 ++++++++++++++++++- settings.gradle.kts | 6 +- 18 files changed, 113 insertions(+), 53 deletions(-) rename {controls-xodus => controls-storage/controls-xodus}/build.gradle.kts (100%) rename {controls-xodus => controls-storage/controls-xodus}/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt (100%) rename {controls-xodus => controls-storage/controls-xodus}/src/test/kotlin/PropertyHistoryTest.kt (100%) rename demo/{ => all-things}/build.gradle.kts (100%) rename demo/{ => all-things}/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt (100%) rename demo/{ => all-things}/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt (100%) rename demo/{ => all-things}/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt (100%) rename demo/{ => all-things}/src/main/kotlin/ru/mipt/npm/controls/demo/generateMessageSchema.kt (100%) diff --git a/controls-xodus/build.gradle.kts b/controls-storage/controls-xodus/build.gradle.kts similarity index 100% rename from controls-xodus/build.gradle.kts rename to controls-storage/controls-xodus/build.gradle.kts diff --git a/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt b/controls-storage/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt similarity index 100% rename from controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt rename to controls-storage/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt diff --git a/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt b/controls-storage/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt similarity index 100% rename from controls-xodus/src/test/kotlin/PropertyHistoryTest.kt rename to controls-storage/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt diff --git a/demo/build.gradle.kts b/demo/all-things/build.gradle.kts similarity index 100% rename from demo/build.gradle.kts rename to demo/all-things/build.gradle.kts diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt b/demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt similarity index 100% rename from demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt rename to demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt b/demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt similarity index 100% rename from demo/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt rename to demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt b/demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt similarity index 100% rename from demo/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt rename to demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt diff --git a/demo/src/main/kotlin/ru/mipt/npm/controls/demo/generateMessageSchema.kt b/demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/generateMessageSchema.kt similarity index 100% rename from demo/src/main/kotlin/ru/mipt/npm/controls/demo/generateMessageSchema.kt rename to demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/generateMessageSchema.kt diff --git a/demo/car/build.gradle.kts b/demo/car/build.gradle.kts index 5bce76e..63d48ed 100644 --- a/demo/car/build.gradle.kts +++ b/demo/car/build.gradle.kts @@ -1,6 +1,6 @@ plugins { kotlin("jvm") - id("org.openjfx.javafxplugin") + id("org.openjfx.javafxplugin") version "0.0.10" application } @@ -19,9 +19,9 @@ dependencies { implementation(projects.magix.magixServer) implementation(projects.magix.magixRsocket) implementation(projects.controlsMagixClient) - implementation(projects.controlsXodus) - implementation(projects.controlsMongo) - implementation(projects.controlsStorage) + implementation(projects.controlsStorage.controlsXodus) + implementation(projects.magix.magixStorage.magixStorageXodus) +// implementation(projects.controlsMongo) implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1") @@ -31,7 +31,7 @@ dependencies { implementation("org.jetbrains.xodus:xodus-entity-store:1.3.232") implementation("org.jetbrains.xodus:xodus-environment:1.3.232") implementation("org.jetbrains.xodus:xodus-vfs:1.3.232") - implementation("org.litote.kmongo:kmongo-coroutine-serialization:4.4.0") +// implementation("org.litote.kmongo:kmongo-coroutine-serialization:4.4.0") } tasks.withType().configureEach { diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt index bee2464..0aaf7ab 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt @@ -1,10 +1,10 @@ package ru.mipt.npm.controls.demo.car -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch -import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.controls.api.PropertyChangedMessage +import ru.mipt.npm.controls.client.controlsMagixFormat import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.api.subscribe import ru.mipt.npm.magix.rsocket.rSocketWithWebSockets import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory @@ -16,31 +16,29 @@ import kotlin.time.ExperimentalTime class MagixVirtualCar(context: Context, meta: Meta) : VirtualCar(context, meta) { - private suspend fun MagixEndpoint.startMagixVirtualCarUpdate() { - launch { - subscribe().collect { magix -> - (magix.payload as? PropertyChangedMessage)?.let { message -> - if (message.sourceDevice == Name.parse("virtual-car")) { - when (message.property) { - "acceleration" -> IVirtualCar.acceleration.write(Vector2D.metaToObject(message.value)) - } + private fun MagixEndpoint.launchMagixVirtualCarUpdate() = launch { + subscribe(controlsMagixFormat).collect { (_, payload) -> + (payload as? PropertyChangedMessage)?.let { message -> + if (message.sourceDevice == Name.parse("virtual-car")) { + when (message.property) { + "acceleration" -> IVirtualCar.acceleration.write(Vector2D.metaToObject(message.value)) } } } } } + @OptIn(ExperimentalTime::class) override suspend fun open() { super.open() val magixEndpoint = MagixEndpoint.rSocketWithWebSockets( meta["magixServerHost"].string ?: "localhost", - DeviceMessage.serializer() ) launch { - magixEndpoint.startMagixVirtualCarUpdate() + magixEndpoint.launchMagixVirtualCarUpdate() } } diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt index 9b9a61e..3e5d968 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt @@ -8,36 +8,29 @@ import javafx.scene.layout.Priority import javafx.stage.Stage import kotlinx.coroutines.Job import kotlinx.coroutines.launch -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.car.IVirtualCar.Companion.acceleration -import ru.mipt.npm.controls.mongo.DefaultAsynchronousMongoClientFactory -import ru.mipt.npm.controls.storage.store +import ru.mipt.npm.controls.manager.DeviceManager +import ru.mipt.npm.controls.manager.install import ru.mipt.npm.controls.storage.storeMessages -import ru.mipt.npm.controls.xodus.XODUS_STORE_PROPERTY -import ru.mipt.npm.controls.xodus.XodusEventStorage +import ru.mipt.npm.controls.xodus.XodusDeviceMessageStorage import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.rsocket.rSocketWithTcp import ru.mipt.npm.magix.server.startMagixServer +import ru.mipt.npm.magix.storage.xodus.storeInXodus import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta import tornadofx.* import java.nio.file.Paths -internal object VirtualCarControllerConfig { - val deviceEntityStorePath = Paths.get(".messages") - val magixEntityStorePath = Paths.get(".server_messages") -} - class VirtualCarController : Controller(), ContextAware { var virtualCar: VirtualCar? = null var magixVirtualCar: MagixVirtualCar? = null var magixServer: ApplicationEngine? = null var xodusStorageJob: Job? = null - var mongoStorageJob: Job? = null + var storageEndpoint: MagixEndpoint? = null + //var mongoStorageJob: Job? = null override val context = Context("demoDevice") { plugin(DeviceManager) @@ -45,7 +38,7 @@ class VirtualCarController : Controller(), ContextAware { private val deviceManager = context.fetch(DeviceManager, Meta { "xodusConfig" put { - "entityStorePath" put VirtualCarControllerConfig.deviceEntityStorePath.toString() + "entityStorePath" put deviceEntityStorePath.toString() } }) @@ -54,19 +47,19 @@ class VirtualCarController : Controller(), ContextAware { virtualCar = deviceManager.install("virtual-car", VirtualCar) //starting magix event loop and connect it to entity store - magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> - store(flow, XodusEventStorage, Meta { - XODUS_STORE_PROPERTY put VirtualCarControllerConfig.magixEntityStorePath.toString() - }) - store(flow, DefaultAsynchronousMongoClientFactory) + magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) + + storageEndpoint = MagixEndpoint.rSocketWithTcp("localhost").apply { + storeInXodus(this@launch, magixEntityStorePath) } + magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) //connect to device entity store - xodusStorageJob = deviceManager.storeMessages(XodusEventStorage) + xodusStorageJob = deviceManager.storeMessages(XodusDeviceMessageStorage) //Create mongo client and connect to MongoDB - mongoStorageJob = deviceManager.storeMessages(DefaultAsynchronousMongoClientFactory) + //mongoStorageJob = deviceManager.storeMessages(DefaultAsynchronousMongoClientFactory) //Launch device client and connect it to the server - val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) + val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost") deviceManager.connectToMagix(deviceEndpoint) } } @@ -81,6 +74,11 @@ class VirtualCarController : Controller(), ContextAware { logger.info { "Virtual car server stopped" } context.close() } + + companion object { + val deviceEntityStorePath = Paths.get(".messages") + val magixEntityStorePath = Paths.get(".server_messages") + } } @@ -113,8 +111,12 @@ class VirtualCarControllerView : View(title = " Virtual car controller remote") action { controller.virtualCar?.run { launch { - acceleration.write(Vector2D(accelerationXProperty.get(), - accelerationYProperty.get())) + acceleration.write( + Vector2D( + accelerationXProperty.get(), + accelerationYProperty.get() + ) + ) } } } diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixFormat.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixFormat.kt index d2aa466..a5df654 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixFormat.kt +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixFormat.kt @@ -34,9 +34,9 @@ public suspend fun MagixEndpoint.broadcast( origin: String = format.defaultFormat, ) { val message = MagixMessage( - origin = origin, - payload = magixJson.encodeToJsonElement(format.serializer, payload), format = format.defaultFormat, + payload = magixJson.encodeToJsonElement(format.serializer, payload), + origin = origin, target = target, id = id, parentId = parentId, diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt index e593434..44e7d6a 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt @@ -25,9 +25,9 @@ import kotlinx.serialization.json.JsonElement */ @Serializable public data class MagixMessage( - val origin: String, + val format: String, val payload: JsonElement, - val format: String = origin, + val origin: String, val target: String? = null, val id: String? = null, val parentId: String? = null, diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt index dbdea23..6c558ad 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt @@ -18,9 +18,9 @@ public fun CoroutineScope.launchMagixConverter( ): Job = endpoint.subscribe(filter).onEach { message-> val newPayload = transformer(message.payload) val transformed: MagixMessage = MagixMessage( - newOrigin ?: message.origin, - newPayload, outputFormat, + newPayload, + newOrigin ?: message.origin, message.target, message.id, message.parentId, diff --git a/magix/magix-demo/src/main/kotlin/zmq.kt b/magix/magix-demo/src/main/kotlin/zmq.kt index 868b503..4672275 100644 --- a/magix/magix-demo/src/main/kotlin/zmq.kt +++ b/magix/magix-demo/src/main/kotlin/zmq.kt @@ -23,7 +23,7 @@ suspend fun MagixEndpoint.sendJson( parentId: String? = null, user: JsonElement? = null, builder: JsonObjectBuilder.() -> Unit -): Unit = broadcast(MagixMessage(origin, buildJsonObject(builder), format, target, id, parentId, user)) +): Unit = broadcast(MagixMessage(format, buildJsonObject(builder), origin, target, id, parentId, user)) internal const val numberOfMessages = 100 diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt index bc84f63..dd4091b 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt +++ b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt @@ -34,7 +34,7 @@ public fun CoroutineScope.launchMagixServerRawRSocket( } /** - * A combined RSocket/TCP server + * A combined RSocket/TCP/ZMQ server * @param applicationConfiguration optional additional configuration for magix loop server */ public fun CoroutineScope.startMagixServer( diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt index 3b98368..e14a1af 100644 --- a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt @@ -1,13 +1,18 @@ package ru.mipt.npm.magix.storage.xodus +import jetbrains.exodus.entitystore.Entity import jetbrains.exodus.entitystore.PersistentEntityStore +import jetbrains.exodus.entitystore.PersistentEntityStores import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.JsonObject import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.api.MagixEndpoint.Companion.magixJson import ru.mipt.npm.magix.api.MagixMessage import ru.mipt.npm.magix.api.MagixMessageFilter +import java.nio.file.Path public class XodusMagixStorage( scope: CoroutineScope, @@ -17,7 +22,7 @@ public class XodusMagixStorage( ) : AutoCloseable { //TODO consider message buffering - private val subscriptionJob = endpoint.subscribe(filter).onEach { message -> + internal val subscriptionJob = endpoint.subscribe(filter).onEach { message -> store.executeInTransaction { transaction -> transaction.newEntity(MAGIC_MESSAGE_ENTITY_TYPE).apply { setProperty(MagixMessage::origin.name, message.origin) @@ -41,6 +46,43 @@ public class XodusMagixStorage( } }.launchIn(scope) + private fun Entity.parseMagixMessage(): MagixMessage = MagixMessage( + format = getProperty(MagixMessage::format.name).toString(), + payload = getBlobString(MagixMessage::payload.name)?.let { + magixJson.parseToJsonElement(it) + } ?: JsonObject(emptyMap()), + origin = getProperty(MagixMessage::origin.name).toString(), + target = getProperty(MagixMessage::target.name)?.toString(), + id = getProperty(MagixMessage::id.name)?.toString(), + parentId = getProperty(MagixMessage::parentId.name)?.toString(), + user = getBlobString(MagixMessage::user.name)?.let { + magixJson.parseToJsonElement(it) + }, + ) + + public fun readByFormat( + format: String, + block: (Sequence) -> Unit, + ): Unit = store.executeInReadonlyTransaction { transaction -> + val sequence = transaction.find( + MAGIC_MESSAGE_ENTITY_TYPE, + MagixMessage::format.name, + format + ).asSequence().map { entity -> + entity.parseMagixMessage() + } + block(sequence) + } + + public fun readAll( + block: (Sequence) -> Unit, + ): Unit = store.executeInReadonlyTransaction { transaction -> + val sequence = transaction.getAll(MAGIC_MESSAGE_ENTITY_TYPE).asSequence().map { entity -> + entity.parseMagixMessage() + } + block(sequence) + } + override fun close() { subscriptionJob.cancel() } @@ -48,4 +90,22 @@ public class XodusMagixStorage( public companion object { public const val MAGIC_MESSAGE_ENTITY_TYPE: String = "magix.message" } +} + +/** + * Start writing all incoming messages with given [filter] to [xodusStore] + */ +public fun MagixEndpoint.storeInXodus( + scope: CoroutineScope, + xodusStore: PersistentEntityStore, + filter: MagixMessageFilter = MagixMessageFilter(), +): XodusMagixStorage = XodusMagixStorage(scope, xodusStore, this, filter) + +public fun MagixEndpoint.storeInXodus( + scope: CoroutineScope, + path: Path, + filter: MagixMessageFilter = MagixMessageFilter(), +): XodusMagixStorage { + val store = PersistentEntityStores.newInstance(path.toFile()) + return XodusMagixStorage(scope, store, this, filter) } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index e49920d..40e069f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -44,9 +44,9 @@ include( ":controls-serial", ":controls-server", ":controls-opcua", - ":controls-xodus", // ":controls-mongo", ":controls-storage", + ":controls-storage:controls-xodus", ":magix", ":magix:magix-api", ":magix:magix-server", @@ -58,6 +58,6 @@ include( ":magix:magix-storage:magix-storage-xodus", ":controls-magix-client", ":motors", - ":demo", -// ":demo:car", + ":demo:all-things", + ":demo:car", ) From cfaeb964e724375b9ce7adf83c7ea9cc6bfa0832 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 5 Jun 2022 19:06:50 +0300 Subject: [PATCH 58/74] Add benchmark demo. Fix some issues with RSocket --- .../ru/mipt/npm/controls/ports/TcpPort.kt | 1 + demo/echo/build.gradle.kts | 32 +++++++ .../ru/mipt/npm/controls/demo/echo/main.kt | 85 ++++++++++++++++++ {motors => demo/motors}/build.gradle.kts | 0 .../C885T0002-TN-C-885.PIMotionMaster-EN.pdf | Bin .../pimotionmaster/PiMotionMasterApp.kt | 0 .../pimotionmaster/PiMotionMasterDevice.kt | 0 .../PiMotionMasterVirtualDevice.kt | 0 .../pimotionmaster/fxDeviceProperties.kt | 0 .../devices/pimotionmaster/piDebugServer.kt | 0 magix/magix-demo/src/main/kotlin/zmq.kt | 2 +- .../npm/magix/rsocket/RSocketMagixEndpoint.kt | 20 +++-- .../rsocket/RSocketStreamMagixEndpoint.kt | 57 ++++++++++++ .../ru/mipt/npm/magix/rsocket/withTcp.kt | 17 ++++ .../ru/mipt/npm/magix/server/magixModule.kt | 31 ++++--- .../kotlin/ru/mipt/npm/magix/server/server.kt | 8 +- .../magix-storage-mongo}/build.gradle.kts | 0 .../npm/controls/mongo/MongoEventStorage.kt | 0 .../ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt | 15 ++-- settings.gradle.kts | 3 +- 20 files changed, 237 insertions(+), 34 deletions(-) create mode 100644 demo/echo/build.gradle.kts create mode 100644 demo/echo/src/main/kotlin/ru/mipt/npm/controls/demo/echo/main.kt rename {motors => demo/motors}/build.gradle.kts (100%) rename {motors => demo/motors}/docs/C885T0002-TN-C-885.PIMotionMaster-EN.pdf (100%) rename {motors => demo/motors}/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt (100%) rename {motors => demo/motors}/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt (100%) rename {motors => demo/motors}/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt (100%) rename {motors => demo/motors}/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt (100%) rename {motors => demo/motors}/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/piDebugServer.kt (100%) create mode 100644 magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketStreamMagixEndpoint.kt rename {controls-mongo => magix/magix-storage/magix-storage-mongo}/build.gradle.kts (100%) rename {controls-mongo => magix/magix-storage/magix-storage-mongo}/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt (100%) diff --git a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/ports/TcpPort.kt b/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/ports/TcpPort.kt index cfd810b..da41a47 100644 --- a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/ports/TcpPort.kt +++ b/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/ports/TcpPort.kt @@ -62,6 +62,7 @@ public class TcpPort private constructor( futureChannel.await().write(ByteBuffer.wrap(data)) } + @OptIn(ExperimentalCoroutinesApi::class) override fun close() { listenerJob.cancel() if(futureChannel.isCompleted){ diff --git a/demo/echo/build.gradle.kts b/demo/echo/build.gradle.kts new file mode 100644 index 0000000..e0cc749 --- /dev/null +++ b/demo/echo/build.gradle.kts @@ -0,0 +1,32 @@ +plugins { + kotlin("jvm") + application +} + +repositories { + mavenCentral() + maven("https://repo.kotlin.link") +} + +val ktorVersion: String by rootProject.extra +val rsocketVersion: String by rootProject.extra + +dependencies { + implementation(projects.magix.magixServer) + implementation(projects.magix.magixRsocket) + implementation(projects.magix.magixZmq) + implementation("io.ktor:ktor-client-cio:$ktorVersion") + + implementation("ch.qos.logback:logback-classic:1.2.11") +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "11" + freeCompilerArgs = freeCompilerArgs + listOf("-Xjvm-default=all", "-Xopt-in=kotlin.RequiresOptIn") + } +} + +application { + mainClass.set("ru.mipt.npm.controls.demo.echo.MainKt") +} \ No newline at end of file diff --git a/demo/echo/src/main/kotlin/ru/mipt/npm/controls/demo/echo/main.kt b/demo/echo/src/main/kotlin/ru/mipt/npm/controls/demo/echo/main.kt new file mode 100644 index 0000000..31d33d5 --- /dev/null +++ b/demo/echo/src/main/kotlin/ru/mipt/npm/controls/demo/echo/main.kt @@ -0,0 +1,85 @@ +package ru.mipt.npm.controls.demo.echo + +import io.ktor.server.application.log +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.serialization.json.JsonObject +import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.api.MagixMessage +import ru.mipt.npm.magix.api.MagixMessageFilter +import ru.mipt.npm.magix.rsocket.rSocketStreamWithTcp +import ru.mipt.npm.magix.server.startMagixServer +import kotlin.time.ExperimentalTime +import kotlin.time.measureTime + +private suspend fun MagixEndpoint.collectEcho(scope: CoroutineScope, n: Int) { + val complete = CompletableDeferred() + + val responseIds = HashSet() + + scope.launch { + subscribe( + MagixMessageFilter( + origin = listOf("loop") + ) + ).collect { message -> + if (message.id?.endsWith(".response") == true) { + responseIds.add(message.parentId!!) + } + val parentId = message.parentId + if (parentId != null && parentId.toInt() >= n - 1) { + println("Losses ${(1 - responseIds.size.toDouble() / n) * 100}%") + complete.complete(true) + cancel() + } + } + } + + scope.launch { + repeat(n) { + if (it % 20 == 0) delay(1) + broadcast( + MagixMessage( + format = "test", + payload = JsonObject(emptyMap()), + origin = "test", + target = "loop", + id = it.toString() + ) + ) + } + } + + complete.await() + println("completed") +} + + +@OptIn(ExperimentalTime::class) +suspend fun main(): Unit = coroutineScope { + launch(Dispatchers.Default) { + val server = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> + //echo each message + flow.onEach { message -> + if (message.parentId == null) { + val m = message.copy(origin = "loop", parentId = message.id, id = message.id + ".response") + log.info("echo: $m") + flow.emit(m) + } + }.launchIn(this) + } + + + val responseTime = measureTime { + MagixEndpoint.rSocketStreamWithTcp("localhost").use { + it.collectEcho(this, 5000) + } + } + + println(responseTime) + + server.stop(500, 500) + cancel() + } +} \ No newline at end of file diff --git a/motors/build.gradle.kts b/demo/motors/build.gradle.kts similarity index 100% rename from motors/build.gradle.kts rename to demo/motors/build.gradle.kts diff --git a/motors/docs/C885T0002-TN-C-885.PIMotionMaster-EN.pdf b/demo/motors/docs/C885T0002-TN-C-885.PIMotionMaster-EN.pdf similarity index 100% rename from motors/docs/C885T0002-TN-C-885.PIMotionMaster-EN.pdf rename to demo/motors/docs/C885T0002-TN-C-885.PIMotionMaster-EN.pdf diff --git a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt similarity index 100% rename from motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt rename to demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt diff --git a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt similarity index 100% rename from motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt rename to demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt diff --git a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt similarity index 100% rename from motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt rename to demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt diff --git a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt similarity index 100% rename from motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt rename to demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt diff --git a/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/piDebugServer.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/piDebugServer.kt similarity index 100% rename from motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/piDebugServer.kt rename to demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/piDebugServer.kt diff --git a/magix/magix-demo/src/main/kotlin/zmq.kt b/magix/magix-demo/src/main/kotlin/zmq.kt index 4672275..c8c4b45 100644 --- a/magix/magix-demo/src/main/kotlin/zmq.kt +++ b/magix/magix-demo/src/main/kotlin/zmq.kt @@ -44,7 +44,7 @@ suspend fun main(): Unit = coroutineScope { logger.info("Starting client") //Create zmq magix endpoint and wait for to finish - ZmqMagixEndpoint("tcp://localhost").use { client -> + ZmqMagixEndpoint("localhost","tcp").use { client -> logger.info("Starting subscription") client.subscribe().onEach { println(it.payload) diff --git a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt index 4b05e6f..255b5bb 100644 --- a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt +++ b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt @@ -2,6 +2,7 @@ package ru.mipt.npm.magix.rsocket import io.ktor.client.HttpClient import io.ktor.client.plugins.websocket.WebSockets +import io.ktor.utils.io.core.Closeable import io.rsocket.kotlin.RSocket import io.rsocket.kotlin.core.RSocketConnector import io.rsocket.kotlin.core.RSocketConnectorBuilder @@ -9,13 +10,10 @@ import io.rsocket.kotlin.ktor.client.RSocketSupport import io.rsocket.kotlin.ktor.client.rSocket import io.rsocket.kotlin.payload.buildPayload import io.rsocket.kotlin.payload.data -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job +import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map -import kotlinx.coroutines.withContext import kotlinx.serialization.encodeToString import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixMessage @@ -27,7 +25,7 @@ import kotlin.coroutines.coroutineContext public class RSocketMagixEndpoint( private val rSocket: RSocket, private val coroutineContext: CoroutineContext, -) : MagixEndpoint { +) : MagixEndpoint, Closeable { override fun subscribe( filter: MagixMessageFilter, @@ -39,11 +37,15 @@ public class RSocketMagixEndpoint( }.filter(filter).flowOn(coroutineContext[CoroutineDispatcher] ?: Dispatchers.Unconfined) } - override suspend fun broadcast(message: MagixMessage) { - withContext(coroutineContext) { - val payload = buildPayload { data(MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message)) } - rSocket.fireAndForget(payload) + override suspend fun broadcast(message: MagixMessage): Unit = withContext(coroutineContext) { + val payload = buildPayload { + data(MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message)) } + rSocket.fireAndForget(payload) + } + + override fun close() { + rSocket.cancel() } public companion object diff --git a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketStreamMagixEndpoint.kt b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketStreamMagixEndpoint.kt new file mode 100644 index 0000000..4b0745e --- /dev/null +++ b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketStreamMagixEndpoint.kt @@ -0,0 +1,57 @@ +package ru.mipt.npm.magix.rsocket + +import io.ktor.utils.io.core.Closeable +import io.rsocket.kotlin.RSocket +import io.rsocket.kotlin.payload.Payload +import io.rsocket.kotlin.payload.buildPayload +import io.rsocket.kotlin.payload.data +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.api.MagixMessage +import ru.mipt.npm.magix.api.MagixMessageFilter +import ru.mipt.npm.magix.api.filter +import kotlin.coroutines.CoroutineContext + +/** + * RSocket endpoint based on established channel + */ +public class RSocketStreamMagixEndpoint( + private val rSocket: RSocket, + private val coroutineContext: CoroutineContext, +) : MagixEndpoint, Closeable { + + private val output: MutableSharedFlow = MutableSharedFlow() + + private val input: Flow by lazy { + rSocket.requestChannel( + Payload.Empty, + output.map { message -> + buildPayload { + data(MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message)) + } + }.flowOn(coroutineContext[CoroutineDispatcher] ?: Dispatchers.Unconfined) + ) + } + + override fun subscribe( + filter: MagixMessageFilter, + ): Flow { + return input.map { + MagixEndpoint.magixJson.decodeFromString(MagixMessage.serializer(), it.data.readText()) + }.filter(filter).flowOn(coroutineContext[CoroutineDispatcher] ?: Dispatchers.Unconfined) + } + + override suspend fun broadcast(message: MagixMessage): Unit { + output.emit(message) + } + + override fun close() { + rSocket.cancel() + } +} \ No newline at end of file diff --git a/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt b/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt index b42ee4f..5d76532 100644 --- a/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt +++ b/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt @@ -25,3 +25,20 @@ public suspend fun MagixEndpoint.Companion.rSocketWithTcp( return RSocketMagixEndpoint(rSocket, coroutineContext) } + + +public suspend fun MagixEndpoint.Companion.rSocketStreamWithTcp( + host: String, + port: Int = DEFAULT_MAGIX_RAW_PORT, + tcpConfig: SocketOptions.TCPClientSocketOptions.() -> Unit = {}, + rSocketConfig: RSocketConnectorBuilder.ConnectionConfigBuilder.() -> Unit = {}, +): RSocketStreamMagixEndpoint { + val transport = TcpClientTransport( + hostname = host, + port = port, + configure = tcpConfig + ) + val rSocket = buildConnector(rSocketConfig).connect(transport) + + return RSocketStreamMagixEndpoint(rSocket, coroutineContext) +} \ No newline at end of file diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt index 70d683f..6cbf00a 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt +++ b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt @@ -5,10 +5,7 @@ import io.ktor.server.application.* import io.ktor.server.html.respondHtml import io.ktor.server.plugins.contentnegotiation.ContentNegotiation import io.ktor.server.request.receive -import io.ktor.server.routing.get -import io.ktor.server.routing.post -import io.ktor.server.routing.route -import io.ktor.server.routing.routing +import io.ktor.server.routing.* import io.ktor.server.util.getValue import io.ktor.server.websocket.WebSockets import io.rsocket.kotlin.ConnectionAcceptor @@ -21,7 +18,6 @@ import io.rsocket.kotlin.payload.data import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* import kotlinx.html.* -import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import ru.mipt.npm.magix.api.MagixEndpoint.Companion.magixJson import ru.mipt.npm.magix.api.MagixMessage @@ -30,27 +26,36 @@ import ru.mipt.npm.magix.api.filter import java.util.* -internal fun CoroutineScope.magixAcceptor(magixFlow: MutableSharedFlow) = ConnectionAcceptor { - RSocketRequestHandler { +internal fun CoroutineScope.magixAcceptor( + magixFlow: MutableSharedFlow, +): ConnectionAcceptor = ConnectionAcceptor { + RSocketRequestHandler(coroutineContext) { //handler for request/stream requestStream { request: Payload -> val filter = magixJson.decodeFromString(MagixMessageFilter.serializer(), request.data.readText()) magixFlow.filter(filter).map { message -> - val string = magixJson.encodeToString(message) + val string = magixJson.encodeToString(MagixMessage.serializer(), message) buildPayload { data(string) } } } + //single send fireAndForget { request: Payload -> - val message = magixJson.decodeFromString(request.data.readText()) + val message = magixJson.decodeFromString(MagixMessage.serializer(), request.data.readText()) magixFlow.emit(message) } // bi-directional connection requestChannel { request: Payload, input: Flow -> input.onEach { - magixFlow.emit(magixJson.decodeFromString(it.data.readText())) - }.launchIn(this@magixAcceptor) + magixFlow.emit(magixJson.decodeFromString(MagixMessage.serializer(), it.data.readText())) + }.launchIn(this) - val filter = magixJson.decodeFromString(MagixMessageFilter.serializer(), request.data.readText()) + val filterText = request.data.readText() + + val filter = if(filterText.isNotBlank()){ + magixJson.decodeFromString(MagixMessageFilter.serializer(), filterText) + } else { + MagixMessageFilter() + } magixFlow.filter(filter).map { message -> val string = magixJson.encodeToString(message) @@ -144,7 +149,7 @@ public fun Application.magixModule(magixFlow: MutableSharedFlow, r magixFlow.emit(message) } //rSocket server. Filter from Payload - rSocket("rsocket", acceptor = this@magixModule.magixAcceptor(magixFlow)) + rSocket("rsocket", acceptor = application.magixAcceptor(magixFlow)) } } } diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt index dd4091b..539f439 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt +++ b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt @@ -9,6 +9,7 @@ import io.rsocket.kotlin.transport.ktor.tcp.TcpServer import io.rsocket.kotlin.transport.ktor.tcp.TcpServerTransport import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import org.slf4j.LoggerFactory import ru.mipt.npm.magix.api.MagixEndpoint @@ -39,15 +40,16 @@ public fun CoroutineScope.launchMagixServerRawRSocket( */ public fun CoroutineScope.startMagixServer( port: Int = DEFAULT_MAGIX_HTTP_PORT, - buffer: Int = 100, + buffer: Int = 1000, enableRawRSocket: Boolean = true, enableZmq: Boolean = true, applicationConfiguration: Application.(MutableSharedFlow) -> Unit = {}, ): ApplicationEngine { val logger = LoggerFactory.getLogger("magix-server") val magixFlow = MutableSharedFlow( - buffer, - extraBufferCapacity = buffer + replay = buffer, + extraBufferCapacity = buffer, + onBufferOverflow = BufferOverflow.DROP_OLDEST ) if (enableRawRSocket) { diff --git a/controls-mongo/build.gradle.kts b/magix/magix-storage/magix-storage-mongo/build.gradle.kts similarity index 100% rename from controls-mongo/build.gradle.kts rename to magix/magix-storage/magix-storage-mongo/build.gradle.kts diff --git a/controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt b/magix/magix-storage/magix-storage-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt similarity index 100% rename from controls-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt rename to magix/magix-storage/magix-storage-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt diff --git a/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt b/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt index 56846e9..b621a69 100644 --- a/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt +++ b/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt @@ -4,8 +4,6 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.flowOn -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString import org.zeromq.SocketType import org.zeromq.ZContext import org.zeromq.ZMQ @@ -19,6 +17,7 @@ import kotlin.coroutines.coroutineContext public class ZmqMagixEndpoint( private val host: String, + private val protocol: String, private val pubPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT, private val pullPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, private val coroutineContext: CoroutineContext = Dispatchers.IO, @@ -28,7 +27,7 @@ public class ZmqMagixEndpoint( @OptIn(ExperimentalCoroutinesApi::class) override fun subscribe(filter: MagixMessageFilter): Flow { val socket = zmqContext.createSocket(SocketType.SUB) - socket.connect("$host:$pubPort") + socket.connect("$protocol://$host:$pubPort") socket.subscribe("") return channelFlow { @@ -40,7 +39,7 @@ public class ZmqMagixEndpoint( //This is a blocking call. val string: String? = socket.recvStr() if (string != null) { - val message = MagixEndpoint.magixJson.decodeFromString(string) + val message = MagixEndpoint.magixJson.decodeFromString(MagixMessage.serializer(), string) send(message) } } catch (t: Throwable) { @@ -58,12 +57,12 @@ public class ZmqMagixEndpoint( private val publishSocket by lazy { zmqContext.createSocket(SocketType.PUSH).apply { - connect("$host:$pullPort") + connect("$protocol://$host:$pullPort") } } override suspend fun broadcast(message: MagixMessage): Unit = withContext(coroutineContext) { - val string = MagixEndpoint.magixJson.encodeToString(message) + val string = MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message) publishSocket.send(string) } @@ -72,12 +71,14 @@ public class ZmqMagixEndpoint( } } -public suspend fun MagixEndpoint.Companion.zmq( +public suspend fun MagixEndpoint.Companion.zmq( host: String, + protocol: String = "tcp", pubPort: Int = DEFAULT_MAGIX_ZMQ_PUB_PORT, pullPort: Int = DEFAULT_MAGIX_ZMQ_PULL_PORT, ): ZmqMagixEndpoint = ZmqMagixEndpoint( host, + protocol, pubPort, pullPort, coroutineContext = coroutineContext diff --git a/settings.gradle.kts b/settings.gradle.kts index 40e069f..e587c96 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -57,7 +57,8 @@ include( // ":magix:magix-storage", ":magix:magix-storage:magix-storage-xodus", ":controls-magix-client", - ":motors", ":demo:all-things", ":demo:car", + ":demo:motors", + ":demo:echo" ) From 535e44286c6e1d17e1fd22ed9724338a89204d8f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 6 Jun 2022 10:57:24 +0300 Subject: [PATCH 59/74] Add port configuration to magix server --- .../ru/mipt/npm/controls/demo/echo/main.kt | 6 +-- {magix => demo}/magix-demo/build.gradle.kts | 0 .../magix-demo/src/main/kotlin/zmq.kt | 0 .../ru/mipt/npm/magix/api/MagixEndpoint.kt | 5 +++ magix/magix-rabbit/build.gradle.kts | 13 ++++++ .../npm/magix/rsocket/RSocketMagixEndpoint.kt | 3 +- .../rsocket/RSocketStreamMagixEndpoint.kt | 45 ++++++++++++++++++- .../kotlin/ru/mipt/npm/magix/server/server.kt | 6 +-- settings.gradle.kts | 5 ++- 9 files changed, 72 insertions(+), 11 deletions(-) rename {magix => demo}/magix-demo/build.gradle.kts (100%) rename {magix => demo}/magix-demo/src/main/kotlin/zmq.kt (100%) create mode 100644 magix/magix-rabbit/build.gradle.kts diff --git a/demo/echo/src/main/kotlin/ru/mipt/npm/controls/demo/echo/main.kt b/demo/echo/src/main/kotlin/ru/mipt/npm/controls/demo/echo/main.kt index 31d33d5..9467c47 100644 --- a/demo/echo/src/main/kotlin/ru/mipt/npm/controls/demo/echo/main.kt +++ b/demo/echo/src/main/kotlin/ru/mipt/npm/controls/demo/echo/main.kt @@ -8,7 +8,7 @@ import kotlinx.serialization.json.JsonObject import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixMessage import ru.mipt.npm.magix.api.MagixMessageFilter -import ru.mipt.npm.magix.rsocket.rSocketStreamWithTcp +import ru.mipt.npm.magix.rsocket.rSocketStreamWithWebSockets import ru.mipt.npm.magix.server.startMagixServer import kotlin.time.ExperimentalTime import kotlin.time.measureTime @@ -59,7 +59,7 @@ private suspend fun MagixEndpoint.collectEcho(scope: CoroutineScope, n: Int) { @OptIn(ExperimentalTime::class) suspend fun main(): Unit = coroutineScope { launch(Dispatchers.Default) { - val server = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> + val server = startMagixServer(enableRawRSocket = false, enableZmq = false) { flow -> //echo each message flow.onEach { message -> if (message.parentId == null) { @@ -72,7 +72,7 @@ suspend fun main(): Unit = coroutineScope { val responseTime = measureTime { - MagixEndpoint.rSocketStreamWithTcp("localhost").use { + MagixEndpoint.rSocketStreamWithWebSockets("localhost").use { it.collectEcho(this, 5000) } } diff --git a/magix/magix-demo/build.gradle.kts b/demo/magix-demo/build.gradle.kts similarity index 100% rename from magix/magix-demo/build.gradle.kts rename to demo/magix-demo/build.gradle.kts diff --git a/magix/magix-demo/src/main/kotlin/zmq.kt b/demo/magix-demo/src/main/kotlin/zmq.kt similarity index 100% rename from magix/magix-demo/src/main/kotlin/zmq.kt rename to demo/magix-demo/src/main/kotlin/zmq.kt diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixEndpoint.kt b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixEndpoint.kt index c531583..52d591f 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixEndpoint.kt +++ b/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixEndpoint.kt @@ -23,6 +23,11 @@ public interface MagixEndpoint { message: MagixMessage, ) + /** + * Close the endpoint and the associated connection if it exists + */ + public fun close() + public companion object { /** * A default port for HTTP/WS connections diff --git a/magix/magix-rabbit/build.gradle.kts b/magix/magix-rabbit/build.gradle.kts new file mode 100644 index 0000000..c9d4bef --- /dev/null +++ b/magix/magix-rabbit/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + id("ru.mipt.npm.gradle.jvm") + `maven-publish` +} + +description = """ + RabbitMQ client magix endpoint +""".trimIndent() + +dependencies { + api(projects.magix.magixApi) + implementation("com.rabbitmq:amqp-client:5.14.2") +} diff --git a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt index 255b5bb..cb165cf 100644 --- a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt +++ b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt @@ -14,7 +14,6 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map -import kotlinx.serialization.encodeToString import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixMessage import ru.mipt.npm.magix.api.MagixMessageFilter @@ -30,7 +29,7 @@ public class RSocketMagixEndpoint( override fun subscribe( filter: MagixMessageFilter, ): Flow { - val payload = buildPayload { data(MagixEndpoint.magixJson.encodeToString(filter)) } + val payload = buildPayload { data(MagixEndpoint.magixJson.encodeToString(MagixMessageFilter.serializer(), filter)) } val flow = rSocket.requestStream(payload) return flow.map { MagixEndpoint.magixJson.decodeFromString(MagixMessage.serializer(), it.data.readText()) diff --git a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketStreamMagixEndpoint.kt b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketStreamMagixEndpoint.kt index 4b0745e..25f99bb 100644 --- a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketStreamMagixEndpoint.kt +++ b/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketStreamMagixEndpoint.kt @@ -1,12 +1,18 @@ package ru.mipt.npm.magix.rsocket +import io.ktor.client.HttpClient +import io.ktor.client.plugins.websocket.WebSockets import io.ktor.utils.io.core.Closeable import io.rsocket.kotlin.RSocket +import io.rsocket.kotlin.core.RSocketConnectorBuilder +import io.rsocket.kotlin.ktor.client.RSocketSupport +import io.rsocket.kotlin.ktor.client.rSocket import io.rsocket.kotlin.payload.Payload import io.rsocket.kotlin.payload.buildPayload import io.rsocket.kotlin.payload.data import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -17,20 +23,32 @@ import ru.mipt.npm.magix.api.MagixMessage import ru.mipt.npm.magix.api.MagixMessageFilter import ru.mipt.npm.magix.api.filter import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.coroutineContext /** - * RSocket endpoint based on established channel + * RSocket endpoint based on established channel. This way it works a bit faster than [RSocketMagixEndpoint] + * for sending and receiving, but less flexible in terms of filters. One general [streamFilter] could be set + * in constructor and applied on the loop side. Filters in [subscribe] are applied on the endpoint side on top + * of received data. */ public class RSocketStreamMagixEndpoint( private val rSocket: RSocket, private val coroutineContext: CoroutineContext, + public val streamFilter: MagixMessageFilter = MagixMessageFilter(), ) : MagixEndpoint, Closeable { private val output: MutableSharedFlow = MutableSharedFlow() private val input: Flow by lazy { rSocket.requestChannel( - Payload.Empty, + buildPayload { + data( + MagixEndpoint.magixJson.encodeToString( + MagixMessageFilter.serializer(), + streamFilter + ) + ) + }, output.map { message -> buildPayload { data(MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message)) @@ -54,4 +72,27 @@ public class RSocketStreamMagixEndpoint( override fun close() { rSocket.cancel() } +} + +public suspend fun MagixEndpoint.Companion.rSocketStreamWithWebSockets( + host: String, + port: Int = DEFAULT_MAGIX_HTTP_PORT, + path: String = "/rsocket", + rSocketConfig: RSocketConnectorBuilder.ConnectionConfigBuilder.() -> Unit = {}, +): RSocketStreamMagixEndpoint { + val client = HttpClient { + install(WebSockets) + install(RSocketSupport) { + connector = buildConnector(rSocketConfig) + } + } + + val rSocket = client.rSocket(host, port, path) + + //Ensure client is closed after rSocket if finished + rSocket.coroutineContext[Job]?.invokeOnCompletion { + client.close() + } + + return RSocketStreamMagixEndpoint(rSocket, coroutineContext) } \ No newline at end of file diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt index 539f439..e75b594 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt +++ b/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt @@ -42,7 +42,10 @@ public fun CoroutineScope.startMagixServer( port: Int = DEFAULT_MAGIX_HTTP_PORT, buffer: Int = 1000, enableRawRSocket: Boolean = true, + rawRSocketPort: Int = DEFAULT_MAGIX_RAW_PORT, enableZmq: Boolean = true, + zmqPubSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT, + zmqPullSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, applicationConfiguration: Application.(MutableSharedFlow) -> Unit = {}, ): ApplicationEngine { val logger = LoggerFactory.getLogger("magix-server") @@ -54,14 +57,11 @@ public fun CoroutineScope.startMagixServer( if (enableRawRSocket) { //Start tcpRSocket server - val rawRSocketPort = DEFAULT_MAGIX_RAW_PORT logger.info("Starting magix raw rsocket server on port $rawRSocketPort") launchMagixServerRawRSocket(magixFlow, rawRSocketPort) } if (enableZmq) { //Start ZMQ server socket pair - val zmqPubSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT - val zmqPullSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT logger.info("Starting magix zmq server on pub port $zmqPubSocketPort and pull port $zmqPullSocketPort") launchMagixServerZmqSocket( magixFlow, diff --git a/settings.gradle.kts b/settings.gradle.kts index e587c96..aff8bbd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,5 @@ rootProject.name = "controls-kt" + enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") enableFeaturePreview("VERSION_CATALOGS") @@ -53,11 +54,13 @@ include( ":magix:magix-rsocket", ":magix:magix-java-client", ":magix:magix-zmq", - ":magix:magix-demo", + ":magix:magix-rabbit", + // ":magix:magix-storage", ":magix:magix-storage:magix-storage-xodus", ":controls-magix-client", ":demo:all-things", + ":demo:magix-demo", ":demo:car", ":demo:motors", ":demo:echo" From 20345f846b41a8803fc1395243afbd3231ef5253 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Mon, 6 Jun 2022 14:10:48 +0300 Subject: [PATCH 60/74] Add rabbitMQ connector (untested) --- magix/magix-rabbit/build.gradle.kts | 4 + .../npm/magix/rabbit/RabbitMQMagixEndpoint.kt | 80 +++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 magix/magix-rabbit/src/main/kotlin/ru/mipt/npm/magix/rabbit/RabbitMQMagixEndpoint.kt diff --git a/magix/magix-rabbit/build.gradle.kts b/magix/magix-rabbit/build.gradle.kts index c9d4bef..960a121 100644 --- a/magix/magix-rabbit/build.gradle.kts +++ b/magix/magix-rabbit/build.gradle.kts @@ -11,3 +11,7 @@ dependencies { api(projects.magix.magixApi) implementation("com.rabbitmq:amqp-client:5.14.2") } + +readme{ + maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE +} diff --git a/magix/magix-rabbit/src/main/kotlin/ru/mipt/npm/magix/rabbit/RabbitMQMagixEndpoint.kt b/magix/magix-rabbit/src/main/kotlin/ru/mipt/npm/magix/rabbit/RabbitMQMagixEndpoint.kt new file mode 100644 index 0000000..a709df8 --- /dev/null +++ b/magix/magix-rabbit/src/main/kotlin/ru/mipt/npm/magix/rabbit/RabbitMQMagixEndpoint.kt @@ -0,0 +1,80 @@ +package ru.mipt.npm.magix.rabbit + +import com.rabbitmq.client.* +import kotlinx.coroutines.cancel +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.launch +import ru.mipt.npm.magix.api.MagixEndpoint +import ru.mipt.npm.magix.api.MagixMessage +import ru.mipt.npm.magix.api.MagixMessageFilter +import ru.mipt.npm.magix.api.filter +import ru.mipt.npm.magix.rabbit.RabbitMQMagixEndpoint.Companion.DEFAULT_MAGIX_QUEUE_NAME + +/** + * A magix endpoint for RabbitMQ message broker + */ +public class RabbitMQMagixEndpoint( + private val connection: Connection, + private val queueName: String = DEFAULT_MAGIX_QUEUE_NAME, +) : MagixEndpoint, AutoCloseable { + + private val rabbitChannel by lazy { + connection.createChannel().apply { + queueDeclare(queueName, false, false, false, null) + } + } + + override fun subscribe(filter: MagixMessageFilter): Flow = callbackFlow { + val deliverCallback: DeliverCallback = DeliverCallback { _: String, message: Delivery -> + val magixMessage = MagixEndpoint.magixJson.decodeFromString( + MagixMessage.serializer(), message.body.decodeToString() + ) + launch { + send(magixMessage) + } + } + + val cancelCallback: CancelCallback = CancelCallback { + cancel("Rabbit consumer is closed") + } + + val consumerTag = rabbitChannel.basicConsume( + queueName, + true, + deliverCallback, + cancelCallback + ) + + awaitClose { + rabbitChannel.basicCancel(consumerTag) + } + }.filter(filter) + + override suspend fun broadcast(message: MagixMessage) { + rabbitChannel.basicPublish( + "", + queueName, + null, + MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message).encodeToByteArray() + ) + } + + override fun close() { + rabbitChannel.close() + connection.close() + } + + public companion object { + public const val DEFAULT_MAGIX_QUEUE_NAME: String = "magix" + } +} + +public fun MagixEndpoint.Companion.rabbit( + address: String, + queueName: String = DEFAULT_MAGIX_QUEUE_NAME, +): RabbitMQMagixEndpoint { + val connection = ConnectionFactory().newConnection(address) + return RabbitMQMagixEndpoint(connection, queueName) +} \ No newline at end of file From bd29789545e18a3deff550643b1e5b331990a4ee Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 2 Aug 2022 09:10:02 +0300 Subject: [PATCH 61/74] Change package name to space.kscience --- build.gradle.kts | 6 ++--- .../kscience}/controls/api/Device.kt | 4 ++-- .../kscience}/controls/api/DeviceHub.kt | 2 +- .../kscience}/controls/api/DeviceMessage.kt | 2 +- .../kscience}/controls/api/Socket.kt | 3 +-- .../kscience}/controls/api/descriptors.kt | 2 +- .../controls/manager/DeviceManager.kt | 6 ++--- .../controls/manager/deviceMessages.kt | 4 ++-- .../kscience}/controls/ports/Port.kt | 4 ++-- .../kscience}/controls/ports/PortProxy.kt | 3 +-- .../controls/ports/SynchronousPortHandler.kt | 2 +- .../kscience}/controls/ports/phrases.kt | 2 +- .../kscience}/controls/spec/DeviceBase.kt | 4 ++-- .../controls/spec/DeviceMetaPropertySpec.kt | 6 ++--- .../controls/spec/DevicePropertySpec.kt | 10 ++++----- .../kscience}/controls/spec/DeviceSpec.kt | 8 +++---- .../controls/spec/deviceExtensions.kt | 2 +- .../kscience}/controls/spec/misc.kt | 2 +- .../controls/spec/propertySpecDelegates.kt | 6 ++--- .../kscience}/controls/misc/javaTimeMeta.kt | 2 +- .../kscience}/controls/ports/TcpPort.kt | 2 +- .../controls/spec/getDeviceProperty.kt | 2 +- .../kscience}/controls/ports/PortIOTest.kt | 2 +- .../controls/client/controlsMagix.kt | 10 ++++----- .../kscience}/controls/client/doocsMagix.kt | 2 +- .../kscience}/controls/client/tangoMagix.kt | 8 +++---- .../controls/opcua/client/MetaBsdParser.kt | 6 ++--- .../controls/opcua/client/MiloDevice.kt | 4 ++-- .../controls/opcua/client/MiloDeviceBySpec.kt | 6 ++--- .../controls/opcua/client/miloClient.kt | 2 +- .../controls/opcua/server/DeviceNameSpace.kt | 12 +++++----- .../controls/opcua/server/metaToOpc.kt | 2 +- .../controls/opcua/server/nodeUtils.kt | 2 +- .../controls/opcua/server/serverUtils.kt | 2 +- .../kscience}/controls/serial/SerialPort.kt | 8 +++---- .../controls/server/deviceWebServer.kt | 14 ++++++------ .../kscience}/controls/server/responses.kt | 4 ++-- .../xodus/XodusDeviceMessageStorage.kt | 8 +++---- .../src/test/kotlin/PropertyHistoryTest.kt | 8 +++---- .../controls/storage/DeviceMessageStorage.kt | 4 ++-- .../controls/storage/storageCommon.kt | 8 +++---- .../kscience}/controls/storage/storageJvm.kt | 2 +- .../controls/storage/workDirectory.kt | 2 +- .../kscience}/controls/ports/KtorTcpPort.kt | 2 +- demo/all-things/build.gradle.kts | 2 +- .../controls/demo/DemoControllerView.kt | 22 +++++++++---------- .../kscience}/controls/demo/DemoDevice.kt | 6 ++--- .../controls/demo/demoDeviceServer.kt | 6 ++--- .../controls/demo/generateMessageSchema.kt | 4 ++-- demo/car/build.gradle.kts | 2 +- .../controls/demo/car/IVirtualCar.kt | 6 ++--- .../controls/demo/car/MagixVirtualCar.kt | 6 ++--- .../kscience}/controls/demo/car/VirtualCar.kt | 6 ++--- .../controls/demo/car/VirtualCarController.kt | 14 ++++++------ demo/echo/build.gradle.kts | 2 +- .../kscience}/controls/demo/echo/main.kt | 2 +- .../pimotionmaster/PiMotionMasterApp.kt | 4 ++-- .../pimotionmaster/PiMotionMasterDevice.kt | 8 +++---- .../PiMotionMasterVirtualDevice.kt | 6 ++--- .../pimotionmaster/fxDeviceProperties.kt | 4 ++-- gradle.properties | 5 ++--- .../npm/controls/mongo/MongoEventStorage.kt | 8 +++---- 62 files changed, 156 insertions(+), 159 deletions(-) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/api/Device.kt (97%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/api/DeviceHub.kt (98%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/api/DeviceMessage.kt (99%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/api/Socket.kt (91%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/api/descriptors.kt (95%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/manager/DeviceManager.kt (93%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/manager/deviceMessages.kt (97%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/ports/Port.kt (95%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/ports/PortProxy.kt (95%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/ports/SynchronousPortHandler.kt (96%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/ports/phrases.kt (97%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/spec/DeviceBase.kt (98%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/spec/DeviceMetaPropertySpec.kt (75%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/spec/DevicePropertySpec.kt (93%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/spec/DeviceSpec.kt (97%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/spec/deviceExtensions.kt (96%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/spec/misc.kt (94%) rename controls-core/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/spec/propertySpecDelegates.kt (96%) rename controls-core/src/jvmMain/kotlin/{ru/mipt/npm => space/kscience}/controls/misc/javaTimeMeta.kt (92%) rename controls-core/src/jvmMain/kotlin/{ru/mipt/npm => space/kscience}/controls/ports/TcpPort.kt (98%) rename controls-core/src/jvmMain/kotlin/{ru/mipt/npm => space/kscience}/controls/spec/getDeviceProperty.kt (69%) rename controls-core/src/jvmTest/kotlin/{ru/mipt/npm => space/kscience}/controls/ports/PortIOTest.kt (95%) rename controls-magix-client/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/client/controlsMagix.kt (87%) rename controls-magix-client/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/client/doocsMagix.kt (99%) rename controls-magix-client/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/client/tangoMagix.kt (96%) rename controls-opcua/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/opcua/client/MetaBsdParser.kt (98%) rename controls-opcua/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/opcua/client/MiloDevice.kt (96%) rename controls-opcua/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/opcua/client/MiloDeviceBySpec.kt (93%) rename controls-opcua/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/opcua/client/miloClient.kt (98%) rename controls-opcua/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/opcua/server/DeviceNameSpace.kt (95%) rename controls-opcua/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/opcua/server/metaToOpc.kt (96%) rename controls-opcua/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/opcua/server/nodeUtils.kt (98%) rename controls-opcua/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/opcua/server/serverUtils.kt (96%) rename controls-serial/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/serial/SerialPort.kt (93%) rename controls-server/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/server/deviceWebServer.kt (95%) rename controls-server/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/server/responses.kt (91%) rename controls-storage/controls-xodus/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/xodus/XodusDeviceMessageStorage.kt (96%) rename controls-storage/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/storage/DeviceMessageStorage.kt (84%) rename controls-storage/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/controls/storage/storageCommon.kt (91%) rename controls-storage/src/jvmMain/kotlin/{ru/mipt/npm => space/kscience}/controls/storage/storageJvm.kt (97%) rename controls-storage/src/jvmMain/kotlin/{ru/mipt/npm => space/kscience}/controls/storage/workDirectory.kt (95%) rename controls-tcp/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/ports/KtorTcpPort.kt (98%) rename demo/all-things/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/demo/DemoControllerView.kt (87%) rename demo/all-things/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/demo/DemoDevice.kt (95%) rename demo/all-things/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/demo/demoDeviceServer.kt (96%) rename demo/all-things/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/demo/generateMessageSchema.kt (72%) rename demo/car/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/demo/car/IVirtualCar.kt (81%) rename demo/car/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/demo/car/MagixVirtualCar.kt (90%) rename demo/car/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/demo/car/VirtualCar.kt (96%) rename demo/car/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/demo/car/VirtualCarController.kt (92%) rename demo/echo/src/main/kotlin/{ru/mipt/npm => space/kscience}/controls/demo/echo/main.kt (98%) diff --git a/build.gradle.kts b/build.gradle.kts index 8a4a56f..48be680 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,13 +7,13 @@ val ktorVersion: String by extra(ru.mipt.npm.gradle.KScienceVersions.ktorVersion val rsocketVersion by extra("0.15.4") allprojects { - group = "ru.mipt.npm" - version = "0.1.1" + group = "space.kscience" + version = "0.1.1-SNAPSHOT" } ksciencePublish { github("controls.kt") - space() + space("https://maven.pkg.jetbrains.space/mipt-npm/p/controls/maven") } apiValidation { diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Device.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Device.kt similarity index 97% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Device.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/api/Device.kt index bd85ae2..223bd0c 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Device.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Device.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.api +package space.kscience.controls.api import io.ktor.utils.io.core.Closeable import kotlinx.coroutines.CoroutineScope @@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import ru.mipt.npm.controls.api.Device.Companion.DEVICE_TARGET +import space.kscience.controls.api.Device.Companion.DEVICE_TARGET import space.kscience.dataforge.context.ContextAware import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.misc.Type diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceHub.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt similarity index 98% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceHub.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt index aba8517..9565950 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceHub.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.api +package space.kscience.controls.api import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.* diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt similarity index 99% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt index 9d9bc19..8f6f0f9 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/DeviceMessage.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.api +package space.kscience.controls.api import kotlinx.datetime.Clock import kotlinx.datetime.Instant diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Socket.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Socket.kt similarity index 91% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Socket.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/api/Socket.kt index eda8942..4d1bb1e 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/Socket.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Socket.kt @@ -1,10 +1,9 @@ -package ru.mipt.npm.controls.api +package space.kscience.controls.api import io.ktor.utils.io.core.Closeable import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch /** diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/descriptors.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/descriptors.kt similarity index 95% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/descriptors.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/api/descriptors.kt index 1e70962..8e1705b 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/api/descriptors.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/descriptors.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.api +package space.kscience.controls.api import kotlinx.serialization.Serializable import space.kscience.dataforge.meta.descriptors.MetaDescriptor diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/manager/DeviceManager.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/manager/DeviceManager.kt similarity index 93% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/manager/DeviceManager.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/manager/DeviceManager.kt index 3194f78..2e7d186 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/manager/DeviceManager.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/manager/DeviceManager.kt @@ -1,8 +1,8 @@ -package ru.mipt.npm.controls.manager +package space.kscience.controls.manager import kotlinx.coroutines.launch -import ru.mipt.npm.controls.api.Device -import ru.mipt.npm.controls.api.DeviceHub +import space.kscience.controls.api.Device +import space.kscience.controls.api.DeviceHub import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MutableMeta diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/manager/deviceMessages.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/manager/deviceMessages.kt similarity index 97% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/manager/deviceMessages.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/manager/deviceMessages.kt index 6d47dba..ea5d34c 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/manager/deviceMessages.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/manager/deviceMessages.kt @@ -1,11 +1,11 @@ -package ru.mipt.npm.controls.manager +package space.kscience.controls.manager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import ru.mipt.npm.controls.api.* +import space.kscience.controls.api.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.plus diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/ports/Port.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt similarity index 95% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/ports/Port.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt index 4cf672d..e7f9504 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/ports/Port.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt @@ -1,10 +1,10 @@ -package ru.mipt.npm.controls.ports +package space.kscience.controls.ports import kotlinx.coroutines.* import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.receiveAsFlow -import ru.mipt.npm.controls.api.Socket +import space.kscience.controls.api.Socket import space.kscience.dataforge.context.* import kotlin.coroutines.CoroutineContext diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/ports/PortProxy.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/PortProxy.kt similarity index 95% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/ports/PortProxy.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/ports/PortProxy.kt index 686992d..4e51f6f 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/ports/PortProxy.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/PortProxy.kt @@ -1,8 +1,7 @@ -package ru.mipt.npm.controls.ports +package space.kscience.controls.ports import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.flow import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/ports/SynchronousPortHandler.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/SynchronousPortHandler.kt similarity index 96% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/ports/SynchronousPortHandler.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/ports/SynchronousPortHandler.kt index 508ce6d..6dd8849 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/ports/SynchronousPortHandler.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/SynchronousPortHandler.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.ports +package space.kscience.controls.ports import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/ports/phrases.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt similarity index 97% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/ports/phrases.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt index 62d075a..896d87a 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/ports/phrases.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.ports +package space.kscience.controls.ports import io.ktor.utils.io.core.BytePacketBuilder import io.ktor.utils.io.core.readBytes diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceBase.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt similarity index 98% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceBase.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt index 8ba0969..397eff7 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceBase.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.spec +package space.kscience.controls.spec import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob @@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import ru.mipt.npm.controls.api.* +import space.kscience.controls.api.* import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Global import space.kscience.dataforge.meta.Meta diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceMetaPropertySpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceMetaPropertySpec.kt similarity index 75% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceMetaPropertySpec.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceMetaPropertySpec.kt index 9d37ef8..809d940 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceMetaPropertySpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceMetaPropertySpec.kt @@ -1,7 +1,7 @@ -package ru.mipt.npm.controls.spec +package space.kscience.controls.spec -import ru.mipt.npm.controls.api.Device -import ru.mipt.npm.controls.api.PropertyDescriptor +import space.kscience.controls.api.Device +import space.kscience.controls.api.PropertyDescriptor import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.transformations.MetaConverter diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DevicePropertySpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DevicePropertySpec.kt similarity index 93% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DevicePropertySpec.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DevicePropertySpec.kt index 10b3b6b..c1bdc11 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DevicePropertySpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DevicePropertySpec.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.spec +package space.kscience.controls.spec import kotlinx.coroutines.Job import kotlinx.coroutines.flow.filter @@ -6,10 +6,10 @@ import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import ru.mipt.npm.controls.api.ActionDescriptor -import ru.mipt.npm.controls.api.Device -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.api.PropertyDescriptor +import space.kscience.controls.api.ActionDescriptor +import space.kscience.controls.api.Device +import space.kscience.controls.api.PropertyChangedMessage +import space.kscience.controls.api.PropertyDescriptor import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.transformations.MetaConverter diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceSpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt similarity index 97% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceSpec.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt index 9bd8ea2..67552ea 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/DeviceSpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt @@ -1,9 +1,9 @@ -package ru.mipt.npm.controls.spec +package space.kscience.controls.spec import kotlinx.coroutines.withContext -import ru.mipt.npm.controls.api.ActionDescriptor -import ru.mipt.npm.controls.api.Device -import ru.mipt.npm.controls.api.PropertyDescriptor +import space.kscience.controls.api.ActionDescriptor +import space.kscience.controls.api.Device +import space.kscience.controls.api.PropertyDescriptor import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.transformations.MetaConverter import kotlin.properties.PropertyDelegateProvider diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/deviceExtensions.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/deviceExtensions.kt similarity index 96% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/deviceExtensions.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/spec/deviceExtensions.kt index da83fab..3a9c280 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/deviceExtensions.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/deviceExtensions.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.spec +package space.kscience.controls.spec import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/misc.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/misc.kt similarity index 94% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/misc.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/spec/misc.kt index 345f453..dfcc76e 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/misc.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/misc.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.spec +package space.kscience.controls.spec import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.double diff --git a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/propertySpecDelegates.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt similarity index 96% rename from controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/propertySpecDelegates.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt index 56cd0ae..3d701e3 100644 --- a/controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/spec/propertySpecDelegates.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt @@ -1,7 +1,7 @@ -package ru.mipt.npm.controls.spec +package space.kscience.controls.spec -import ru.mipt.npm.controls.api.PropertyDescriptor -import ru.mipt.npm.controls.api.metaDescriptor +import space.kscience.controls.api.PropertyDescriptor +import space.kscience.controls.api.metaDescriptor import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.transformations.MetaConverter import space.kscience.dataforge.values.ValueType diff --git a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/misc/javaTimeMeta.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/misc/javaTimeMeta.kt similarity index 92% rename from controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/misc/javaTimeMeta.kt rename to controls-core/src/jvmMain/kotlin/space/kscience/controls/misc/javaTimeMeta.kt index eec5774..30829b3 100644 --- a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/misc/javaTimeMeta.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/misc/javaTimeMeta.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.misc +package space.kscience.controls.misc import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get diff --git a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/ports/TcpPort.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt similarity index 98% rename from controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/ports/TcpPort.kt rename to controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt index da41a47..2ef0d89 100644 --- a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/ports/TcpPort.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.ports +package space.kscience.controls.ports import kotlinx.coroutines.* import space.kscience.dataforge.context.Context diff --git a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/spec/getDeviceProperty.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/spec/getDeviceProperty.kt similarity index 69% rename from controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/spec/getDeviceProperty.kt rename to controls-core/src/jvmMain/kotlin/space/kscience/controls/spec/getDeviceProperty.kt index 39c92e9..4a783e2 100644 --- a/controls-core/src/jvmMain/kotlin/ru/mipt/npm/controls/spec/getDeviceProperty.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/spec/getDeviceProperty.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.spec +package space.kscience.controls.spec import kotlinx.coroutines.runBlocking diff --git a/controls-core/src/jvmTest/kotlin/ru/mipt/npm/controls/ports/PortIOTest.kt b/controls-core/src/jvmTest/kotlin/space/kscience/controls/ports/PortIOTest.kt similarity index 95% rename from controls-core/src/jvmTest/kotlin/ru/mipt/npm/controls/ports/PortIOTest.kt rename to controls-core/src/jvmTest/kotlin/space/kscience/controls/ports/PortIOTest.kt index cdb3107..bdf6891 100644 --- a/controls-core/src/jvmTest/kotlin/ru/mipt/npm/controls/ports/PortIOTest.kt +++ b/controls-core/src/jvmTest/kotlin/space/kscience/controls/ports/PortIOTest.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.ports +package space.kscience.controls.ports import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map diff --git a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/controlsMagix.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt similarity index 87% rename from controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/controlsMagix.kt rename to controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt index d5fa09d..7355df0 100644 --- a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/controlsMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt @@ -1,15 +1,15 @@ -package ru.mipt.npm.controls.client +package space.kscience.controls.client import kotlinx.coroutines.Job import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.manager.DeviceManager -import ru.mipt.npm.controls.manager.hubMessageFlow -import ru.mipt.npm.controls.manager.respondHubMessage import ru.mipt.npm.magix.api.* +import space.kscience.controls.api.DeviceMessage +import space.kscience.controls.manager.DeviceManager +import space.kscience.controls.manager.hubMessageFlow +import space.kscience.controls.manager.respondHubMessage import space.kscience.dataforge.context.error import space.kscience.dataforge.context.logger diff --git a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/doocsMagix.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/doocsMagix.kt similarity index 99% rename from controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/doocsMagix.kt rename to controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/doocsMagix.kt index 8f42deb..5655189 100644 --- a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/doocsMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/doocsMagix.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.client +package space.kscience.controls.client import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/tangoMagix.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt similarity index 96% rename from controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/tangoMagix.kt rename to controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt index 8a88868..1638d85 100644 --- a/controls-magix-client/src/commonMain/kotlin/ru/mipt/npm/controls/client/tangoMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt @@ -1,14 +1,14 @@ -package ru.mipt.npm.controls.client +package space.kscience.controls.client import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.serialization.Serializable -import ru.mipt.npm.controls.api.get -import ru.mipt.npm.controls.api.getOrReadProperty -import ru.mipt.npm.controls.manager.DeviceManager import ru.mipt.npm.magix.api.* +import space.kscience.controls.api.get +import space.kscience.controls.api.getOrReadProperty +import space.kscience.controls.manager.DeviceManager import space.kscience.dataforge.context.error import space.kscience.dataforge.context.logger import space.kscience.dataforge.meta.Meta diff --git a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MetaBsdParser.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MetaBsdParser.kt similarity index 98% rename from controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MetaBsdParser.kt rename to controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MetaBsdParser.kt index 171b74e..2cfe632 100644 --- a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MetaBsdParser.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MetaBsdParser.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.opcua.client +package space.kscience.controls.opcua.client import org.eclipse.milo.opcua.binaryschema.AbstractCodec import org.eclipse.milo.opcua.binaryschema.parser.BsdParser @@ -11,8 +11,8 @@ import org.eclipse.milo.opcua.stack.core.types.builtin.* import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.* import org.opcfoundation.opcua.binaryschema.EnumeratedType import org.opcfoundation.opcua.binaryschema.StructuredType -import ru.mipt.npm.controls.misc.instant -import ru.mipt.npm.controls.misc.toMeta +import space.kscience.controls.misc.instant +import space.kscience.controls.misc.toMeta import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName diff --git a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDevice.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MiloDevice.kt similarity index 96% rename from controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDevice.kt rename to controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MiloDevice.kt index 04393fc..7172e22 100644 --- a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDevice.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MiloDevice.kt @@ -1,11 +1,11 @@ -package ru.mipt.npm.controls.opcua.client +package space.kscience.controls.opcua.client import kotlinx.coroutines.future.await import kotlinx.serialization.json.Json import org.eclipse.milo.opcua.sdk.client.OpcUaClient import org.eclipse.milo.opcua.stack.core.types.builtin.* import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn -import ru.mipt.npm.controls.api.Device +import space.kscience.controls.api.Device import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MetaSerializer import space.kscience.dataforge.meta.transformations.MetaConverter diff --git a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDeviceBySpec.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MiloDeviceBySpec.kt similarity index 93% rename from controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDeviceBySpec.kt rename to controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MiloDeviceBySpec.kt index d76fc75..cae0f5e 100644 --- a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/MiloDeviceBySpec.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MiloDeviceBySpec.kt @@ -1,11 +1,11 @@ -package ru.mipt.npm.controls.opcua.client +package space.kscience.controls.opcua.client import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import org.eclipse.milo.opcua.sdk.client.OpcUaClient import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId -import ru.mipt.npm.controls.spec.DeviceBySpec -import ru.mipt.npm.controls.spec.DeviceSpec +import space.kscience.controls.spec.DeviceBySpec +import space.kscience.controls.spec.DeviceSpec import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Global import space.kscience.dataforge.meta.Meta diff --git a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/miloClient.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/miloClient.kt similarity index 98% rename from controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/miloClient.kt rename to controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/miloClient.kt index 2d489d6..8415b3a 100644 --- a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/client/miloClient.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/miloClient.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.opcua.client +package space.kscience.controls.opcua.client import org.eclipse.milo.opcua.sdk.client.OpcUaClient import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfigBuilder diff --git a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/DeviceNameSpace.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt similarity index 95% rename from controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/DeviceNameSpace.kt rename to controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt index 8b95d8e..8686764 100644 --- a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/DeviceNameSpace.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.opcua.server +package space.kscience.controls.opcua.server import kotlinx.coroutines.launch import kotlinx.datetime.toJavaInstant @@ -18,11 +18,11 @@ import org.eclipse.milo.opcua.stack.core.AttributeId import org.eclipse.milo.opcua.stack.core.Identifiers import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText -import ru.mipt.npm.controls.api.Device -import ru.mipt.npm.controls.api.DeviceHub -import ru.mipt.npm.controls.api.PropertyDescriptor -import ru.mipt.npm.controls.api.onPropertyChange -import ru.mipt.npm.controls.manager.DeviceManager +import space.kscience.controls.api.Device +import space.kscience.controls.api.DeviceHub +import space.kscience.controls.api.PropertyDescriptor +import space.kscience.controls.api.onPropertyChange +import space.kscience.controls.manager.DeviceManager import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MetaSerializer import space.kscience.dataforge.names.Name diff --git a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/metaToOpc.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/metaToOpc.kt similarity index 96% rename from controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/metaToOpc.kt rename to controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/metaToOpc.kt index cbcd2ec..5294625 100644 --- a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/metaToOpc.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/metaToOpc.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.opcua.server +package space.kscience.controls.opcua.server import kotlinx.serialization.json.Json import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue diff --git a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/nodeUtils.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/nodeUtils.kt similarity index 98% rename from controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/nodeUtils.kt rename to controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/nodeUtils.kt index 783c229..26e0dfe 100644 --- a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/nodeUtils.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/nodeUtils.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.opcua.server +package space.kscience.controls.opcua.server import org.eclipse.milo.opcua.sdk.core.AccessLevel import org.eclipse.milo.opcua.sdk.core.Reference diff --git a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/serverUtils.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/serverUtils.kt similarity index 96% rename from controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/serverUtils.kt rename to controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/serverUtils.kt index 6ba3d36..083d953 100644 --- a/controls-opcua/src/main/kotlin/ru/mipt/npm/controls/opcua/server/serverUtils.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/serverUtils.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.opcua.server +package space.kscience.controls.opcua.server import org.eclipse.milo.opcua.sdk.server.OpcUaServer import org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig diff --git a/controls-serial/src/main/kotlin/ru/mipt/npm/controls/serial/SerialPort.kt b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt similarity index 93% rename from controls-serial/src/main/kotlin/ru/mipt/npm/controls/serial/SerialPort.kt rename to controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt index 1c9ad27..eab88b6 100644 --- a/controls-serial/src/main/kotlin/ru/mipt/npm/controls/serial/SerialPort.kt +++ b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt @@ -1,10 +1,10 @@ -package ru.mipt.npm.controls.serial +package space.kscience.controls.serial import jssc.SerialPort.* import jssc.SerialPortEventListener -import ru.mipt.npm.controls.ports.AbstractPort -import ru.mipt.npm.controls.ports.Port -import ru.mipt.npm.controls.ports.PortFactory +import space.kscience.controls.ports.AbstractPort +import space.kscience.controls.ports.Port +import space.kscience.controls.ports.PortFactory import space.kscience.dataforge.context.Context import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.int diff --git a/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt b/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt similarity index 95% rename from controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt rename to controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt index a2c67dd..881ad1b 100644 --- a/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/deviceWebServer.kt +++ b/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.server +package space.kscience.controls.server import io.ktor.http.HttpStatusCode @@ -26,16 +26,16 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.buildJsonArray import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.json.put -import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.api.PropertyGetMessage -import ru.mipt.npm.controls.api.PropertySetMessage -import ru.mipt.npm.controls.api.getOrNull -import ru.mipt.npm.controls.manager.DeviceManager -import ru.mipt.npm.controls.manager.respondHubMessage import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixMessage import ru.mipt.npm.magix.server.launchMagixServerRawRSocket import ru.mipt.npm.magix.server.magixModule +import space.kscience.controls.api.DeviceMessage +import space.kscience.controls.api.PropertyGetMessage +import space.kscience.controls.api.PropertySetMessage +import space.kscience.controls.api.getOrNull +import space.kscience.controls.manager.DeviceManager +import space.kscience.controls.manager.respondHubMessage import space.kscience.dataforge.meta.toMeta import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName diff --git a/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/responses.kt b/controls-server/src/main/kotlin/space/kscience/controls/server/responses.kt similarity index 91% rename from controls-server/src/main/kotlin/ru/mipt/npm/controls/server/responses.kt rename to controls-server/src/main/kotlin/space/kscience/controls/server/responses.kt index fe56b96..96912e2 100644 --- a/controls-server/src/main/kotlin/ru/mipt/npm/controls/server/responses.kt +++ b/controls-server/src/main/kotlin/space/kscience/controls/server/responses.kt @@ -1,12 +1,12 @@ -package ru.mipt.npm.controls.server +package space.kscience.controls.server import io.ktor.http.ContentType import io.ktor.server.application.ApplicationCall import io.ktor.server.response.respondText import kotlinx.serialization.json.JsonObjectBuilder import kotlinx.serialization.json.buildJsonObject -import ru.mipt.npm.controls.api.DeviceMessage import ru.mipt.npm.magix.api.MagixEndpoint +import space.kscience.controls.api.DeviceMessage //internal fun Frame.toEnvelope(): Envelope { diff --git a/controls-storage/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt b/controls-storage/controls-xodus/src/main/kotlin/space/kscience/controls/xodus/XodusDeviceMessageStorage.kt similarity index 96% rename from controls-storage/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt rename to controls-storage/controls-xodus/src/main/kotlin/space/kscience/controls/xodus/XodusDeviceMessageStorage.kt index fd2f79b..2255d89 100644 --- a/controls-storage/controls-xodus/src/main/kotlin/ru/mipt/npm/controls/xodus/XodusDeviceMessageStorage.kt +++ b/controls-storage/controls-xodus/src/main/kotlin/space/kscience/controls/xodus/XodusDeviceMessageStorage.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.xodus +package space.kscience.controls.xodus import jetbrains.exodus.entitystore.Entity import jetbrains.exodus.entitystore.PersistentEntityStore @@ -11,9 +11,9 @@ import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive -import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.storage.DeviceMessageStorage -import ru.mipt.npm.controls.storage.workDirectory +import space.kscience.controls.api.DeviceMessage +import space.kscience.controls.storage.DeviceMessageStorage +import space.kscience.controls.storage.workDirectory import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.context.fetch diff --git a/controls-storage/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt b/controls-storage/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt index 618569f..1724079 100644 --- a/controls-storage/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt +++ b/controls-storage/controls-xodus/src/test/kotlin/PropertyHistoryTest.kt @@ -6,10 +6,10 @@ import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.xodus.XodusDeviceMessageStorage -import ru.mipt.npm.controls.xodus.query -import ru.mipt.npm.controls.xodus.writeMessage +import space.kscience.controls.api.PropertyChangedMessage +import space.kscience.controls.xodus.XodusDeviceMessageStorage +import space.kscience.controls.xodus.query +import space.kscience.controls.xodus.writeMessage import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName diff --git a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/DeviceMessageStorage.kt b/controls-storage/src/commonMain/kotlin/space/kscience/controls/storage/DeviceMessageStorage.kt similarity index 84% rename from controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/DeviceMessageStorage.kt rename to controls-storage/src/commonMain/kotlin/space/kscience/controls/storage/DeviceMessageStorage.kt index 7c5a8bb..87f4b74 100644 --- a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/DeviceMessageStorage.kt +++ b/controls-storage/src/commonMain/kotlin/space/kscience/controls/storage/DeviceMessageStorage.kt @@ -1,7 +1,7 @@ -package ru.mipt.npm.controls.storage +package space.kscience.controls.storage import kotlinx.datetime.Instant -import ru.mipt.npm.controls.api.DeviceMessage +import space.kscience.controls.api.DeviceMessage import space.kscience.dataforge.names.Name /** diff --git a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt b/controls-storage/src/commonMain/kotlin/space/kscience/controls/storage/storageCommon.kt similarity index 91% rename from controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt rename to controls-storage/src/commonMain/kotlin/space/kscience/controls/storage/storageCommon.kt index 9d9c058..672e2cc 100644 --- a/controls-storage/src/commonMain/kotlin/ru/mipt/npm/controls/storage/storageCommon.kt +++ b/controls-storage/src/commonMain/kotlin/space/kscience/controls/storage/storageCommon.kt @@ -1,13 +1,13 @@ -package ru.mipt.npm.controls.storage +package space.kscience.controls.storage import kotlinx.coroutines.Job import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onEach -import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.manager.DeviceManager -import ru.mipt.npm.controls.manager.hubMessageFlow +import space.kscience.controls.api.DeviceMessage +import space.kscience.controls.manager.DeviceManager +import space.kscience.controls.manager.hubMessageFlow import space.kscience.dataforge.context.Factory import space.kscience.dataforge.context.debug import space.kscience.dataforge.context.logger diff --git a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt b/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/storageJvm.kt similarity index 97% rename from controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt rename to controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/storageJvm.kt index 3960ccd..40811a4 100644 --- a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/storageJvm.kt +++ b/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/storageJvm.kt @@ -1,4 +1,4 @@ -//package ru.mipt.npm.controls.storage +//package space.kscience.controls.storage // //import io.ktor.server.application.Application //import kotlinx.coroutines.InternalCoroutinesApi diff --git a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/workDirectory.kt b/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt similarity index 95% rename from controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/workDirectory.kt rename to controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt index 76d8439..7e4086f 100644 --- a/controls-storage/src/jvmMain/kotlin/ru/mipt/npm/controls/storage/workDirectory.kt +++ b/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.storage +package space.kscience.controls.storage import space.kscience.dataforge.context.ContextBuilder import space.kscience.dataforge.io.IOPlugin diff --git a/controls-tcp/src/main/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt b/controls-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt similarity index 98% rename from controls-tcp/src/main/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt rename to controls-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt index ca2cf61..2c77aa5 100644 --- a/controls-tcp/src/main/kotlin/ru/mipt/npm/controls/ports/KtorTcpPort.kt +++ b/controls-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.ports +package space.kscience.controls.ports import io.ktor.network.selector.ActorSelectorManager import io.ktor.network.sockets.aSocket diff --git a/demo/all-things/build.gradle.kts b/demo/all-things/build.gradle.kts index f4c06ef..53a57e4 100644 --- a/demo/all-things/build.gradle.kts +++ b/demo/all-things/build.gradle.kts @@ -42,5 +42,5 @@ javafx { } application { - mainClass.set("ru.mipt.npm.controls.demo.DemoControllerViewKt") + mainClass.set("space.kscience.controls.demo.DemoControllerViewKt") } \ No newline at end of file diff --git a/demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt similarity index 87% rename from demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt rename to demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt index ba6ff8e..a37430f 100644 --- a/demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/DemoControllerView.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.demo +package space.kscience.controls.demo import io.ktor.server.engine.ApplicationEngine import javafx.scene.Parent @@ -8,19 +8,19 @@ 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.client.connectToMagix -import ru.mipt.npm.controls.demo.DemoDevice.Companion.cosScale -import ru.mipt.npm.controls.demo.DemoDevice.Companion.sinScale -import ru.mipt.npm.controls.demo.DemoDevice.Companion.timeScale -import ru.mipt.npm.controls.manager.DeviceManager -import ru.mipt.npm.controls.manager.install -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.rsocket.rSocketWithWebSockets import ru.mipt.npm.magix.server.startMagixServer +import space.kscience.controls.client.connectToMagix +import space.kscience.controls.demo.DemoDevice.Companion.cosScale +import space.kscience.controls.demo.DemoDevice.Companion.sinScale +import space.kscience.controls.demo.DemoDevice.Companion.timeScale +import space.kscience.controls.manager.DeviceManager +import space.kscience.controls.manager.install +import space.kscience.controls.opcua.server.OpcUaServer +import space.kscience.controls.opcua.server.endpoint +import space.kscience.controls.opcua.server.serveDevices import space.kscience.dataforge.context.* import tornadofx.* import java.awt.Desktop @@ -32,7 +32,7 @@ class DemoController : Controller(), ContextAware { var magixServer: ApplicationEngine? = null var visualizer: ApplicationEngine? = null var opcUaServer: OpcUaServer = OpcUaServer { - setApplicationName(LocalizedText.english("ru.mipt.npm.controls.opcua")) + setApplicationName(LocalizedText.english("space.kscience.controls.opcua")) endpoint { setBindPort(9999) //use default endpoint diff --git a/demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt similarity index 95% rename from demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt rename to demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt index 9bd45cf..26ac20b 100644 --- a/demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/DemoDevice.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt @@ -1,8 +1,8 @@ -package ru.mipt.npm.controls.demo +package space.kscience.controls.demo import kotlinx.coroutines.launch -import ru.mipt.npm.controls.api.metaDescriptor -import ru.mipt.npm.controls.spec.* +import space.kscience.controls.api.metaDescriptor +import space.kscience.controls.spec.* import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta diff --git a/demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt similarity index 96% rename from demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt rename to demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt index 0794f03..29791ae 100644 --- a/demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/demoDeviceServer.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.demo +package space.kscience.controls.demo import io.ktor.server.application.install import io.ktor.server.cio.CIO @@ -11,10 +11,10 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.html.div import kotlinx.html.link -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.client.controlsMagixFormat import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.subscribe +import space.kscience.controls.api.PropertyChangedMessage +import space.kscience.controls.client.controlsMagixFormat import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.double import space.kscience.plotly.layout diff --git a/demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/generateMessageSchema.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/generateMessageSchema.kt similarity index 72% rename from demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/generateMessageSchema.kt rename to demo/all-things/src/main/kotlin/space/kscience/controls/demo/generateMessageSchema.kt index 85522a5..d50ec2c 100644 --- a/demo/all-things/src/main/kotlin/ru/mipt/npm/controls/demo/generateMessageSchema.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/generateMessageSchema.kt @@ -1,8 +1,8 @@ -package ru.mipt.npm.controls.demo +package space.kscience.controls.demo //import com.github.ricky12awesome.jss.encodeToSchema //import com.github.ricky12awesome.jss.globalJson -//import ru.mipt.npm.controls.api.DeviceMessage +//import space.kscience.controls.api.DeviceMessage //fun main() { // val schema = globalJson.encodeToSchema(DeviceMessage.serializer(), generateDefinitions = false) diff --git a/demo/car/build.gradle.kts b/demo/car/build.gradle.kts index 63d48ed..09555d6 100644 --- a/demo/car/build.gradle.kts +++ b/demo/car/build.gradle.kts @@ -47,5 +47,5 @@ javafx { } application { - mainClass.set("ru.mipt.npm.controls.demo.car.VirtualCarControllerKt") + mainClass.set("space.kscience.controls.demo.car.VirtualCarControllerKt") } \ No newline at end of file diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/IVirtualCar.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/IVirtualCar.kt similarity index 81% rename from demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/IVirtualCar.kt rename to demo/car/src/main/kotlin/space/kscience/controls/demo/car/IVirtualCar.kt index b7a2a97..3bb8c79 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/IVirtualCar.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/IVirtualCar.kt @@ -1,7 +1,7 @@ -package ru.mipt.npm.controls.demo.car +package space.kscience.controls.demo.car -import ru.mipt.npm.controls.api.Device -import ru.mipt.npm.controls.spec.DeviceSpec +import space.kscience.controls.api.Device +import space.kscience.controls.spec.DeviceSpec interface IVirtualCar : Device { var speedState: Vector2D diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt similarity index 90% rename from demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt rename to demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt index 0aaf7ab..48eaa15 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/MagixVirtualCar.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt @@ -1,11 +1,11 @@ -package ru.mipt.npm.controls.demo.car +package space.kscience.controls.demo.car import kotlinx.coroutines.launch -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.client.controlsMagixFormat import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.subscribe import ru.mipt.npm.magix.rsocket.rSocketWithWebSockets +import space.kscience.controls.api.PropertyChangedMessage +import space.kscience.controls.client.controlsMagixFormat import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCar.kt similarity index 96% rename from demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt rename to demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCar.kt index 18fc440..a1240a4 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCar.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCar.kt @@ -1,13 +1,13 @@ @file:OptIn(ExperimentalTime::class) -package ru.mipt.npm.controls.demo.car +package space.kscience.controls.demo.car import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.datetime.Clock import kotlinx.datetime.Instant -import ru.mipt.npm.controls.spec.DeviceBySpec -import ru.mipt.npm.controls.spec.doRecurring +import space.kscience.controls.spec.DeviceBySpec +import space.kscience.controls.spec.doRecurring import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta diff --git a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt similarity index 92% rename from demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt rename to demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt index 3e5d968..143969e 100644 --- a/demo/car/src/main/kotlin/ru/mipt/npm/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.demo.car +package space.kscience.controls.demo.car import io.ktor.server.engine.ApplicationEngine import javafx.beans.property.DoubleProperty @@ -8,16 +8,16 @@ import javafx.scene.layout.Priority import javafx.stage.Stage import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import ru.mipt.npm.controls.client.connectToMagix -import ru.mipt.npm.controls.demo.car.IVirtualCar.Companion.acceleration -import ru.mipt.npm.controls.manager.DeviceManager -import ru.mipt.npm.controls.manager.install -import ru.mipt.npm.controls.storage.storeMessages -import ru.mipt.npm.controls.xodus.XodusDeviceMessageStorage import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.rsocket.rSocketWithTcp import ru.mipt.npm.magix.server.startMagixServer import ru.mipt.npm.magix.storage.xodus.storeInXodus +import space.kscience.controls.client.connectToMagix +import space.kscience.controls.demo.car.IVirtualCar.Companion.acceleration +import space.kscience.controls.manager.DeviceManager +import space.kscience.controls.manager.install +import space.kscience.controls.storage.storeMessages +import space.kscience.controls.xodus.XodusDeviceMessageStorage import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta import tornadofx.* diff --git a/demo/echo/build.gradle.kts b/demo/echo/build.gradle.kts index e0cc749..47ac2af 100644 --- a/demo/echo/build.gradle.kts +++ b/demo/echo/build.gradle.kts @@ -28,5 +28,5 @@ tasks.withType().configureEach } application { - mainClass.set("ru.mipt.npm.controls.demo.echo.MainKt") + mainClass.set("space.kscience.controls.demo.echo.MainKt") } \ No newline at end of file diff --git a/demo/echo/src/main/kotlin/ru/mipt/npm/controls/demo/echo/main.kt b/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt similarity index 98% rename from demo/echo/src/main/kotlin/ru/mipt/npm/controls/demo/echo/main.kt rename to demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt index 9467c47..05dab38 100644 --- a/demo/echo/src/main/kotlin/ru/mipt/npm/controls/demo/echo/main.kt +++ b/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.demo.echo +package space.kscience.controls.demo.echo import io.ktor.server.application.log import kotlinx.coroutines.* diff --git a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt index 1476201..37f7323 100644 --- a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt +++ b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt @@ -11,11 +11,11 @@ import javafx.scene.layout.VBox import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import ru.mipt.npm.controls.manager.DeviceManager -import ru.mipt.npm.controls.manager.installing import ru.mipt.npm.devices.pimotionmaster.PiMotionMasterDevice.Axis.Companion.maxPosition import ru.mipt.npm.devices.pimotionmaster.PiMotionMasterDevice.Axis.Companion.minPosition import ru.mipt.npm.devices.pimotionmaster.PiMotionMasterDevice.Axis.Companion.position +import space.kscience.controls.manager.DeviceManager +import space.kscience.controls.manager.installing import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.fetch import tornadofx.* diff --git a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt index 7bdfa9f..ff35345 100644 --- a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt +++ b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt @@ -11,10 +11,10 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withTimeout -import ru.mipt.npm.controls.api.DeviceHub -import ru.mipt.npm.controls.api.PropertyDescriptor -import ru.mipt.npm.controls.ports.* -import ru.mipt.npm.controls.spec.* +import space.kscience.controls.api.DeviceHub +import space.kscience.controls.api.PropertyDescriptor +import space.kscience.controls.ports.* +import space.kscience.controls.spec.* import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.double diff --git a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt index cb490e1..8efe4e9 100644 --- a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt +++ b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice.kt @@ -5,9 +5,9 @@ import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import ru.mipt.npm.controls.api.Socket -import ru.mipt.npm.controls.ports.AbstractPort -import ru.mipt.npm.controls.ports.withDelimiter +import space.kscience.controls.api.Socket +import space.kscience.controls.ports.AbstractPort +import space.kscience.controls.ports.withDelimiter import space.kscience.dataforge.context.* import kotlin.math.abs import kotlin.time.Duration diff --git a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt index 3da23f3..8e399a4 100644 --- a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt +++ b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt @@ -3,8 +3,8 @@ package ru.mipt.npm.devices.pimotionmaster import javafx.beans.property.ObjectPropertyBase import javafx.beans.property.Property import javafx.beans.property.ReadOnlyProperty -import ru.mipt.npm.controls.api.Device -import ru.mipt.npm.controls.spec.* +import space.kscience.controls.api.Device +import space.kscience.controls.spec.* import space.kscience.dataforge.context.info import space.kscience.dataforge.context.logger import tornadofx.* diff --git a/gradle.properties b/gradle.properties index bd1c590..1d1dcc7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,11 +1,10 @@ kotlin.code.style=official kotlin.mpp.stability.nowarn=true - -kotlin.jupyter.add.scanner=false +kotlin.native.ignoreDisabledTargets=true org.gradle.parallel=true publishing.github=false publishing.sonatype=false -toolsVersion=0.11.5-kotlin-1.7.0-RC \ No newline at end of file +toolsVersion=0.11.7-kotlin-1.7.0 \ No newline at end of file diff --git a/magix/magix-storage/magix-storage-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt b/magix/magix-storage/magix-storage-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt index 4bfbafb..5d02e92 100644 --- a/magix/magix-storage/magix-storage-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt +++ b/magix/magix-storage/magix-storage-mongo/src/main/kotlin/ru/mipt/npm/controls/mongo/MongoEventStorage.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.controls.mongo +package space.kscience.controls.mongo import kotlinx.serialization.KSerializer import kotlinx.serialization.json.Json @@ -6,9 +6,9 @@ import org.litote.kmongo.coroutine.CoroutineClient import org.litote.kmongo.coroutine.coroutine import org.litote.kmongo.coroutine.insertOne import org.litote.kmongo.reactivestreams.KMongo -import ru.mipt.npm.controls.api.DeviceMessage -import ru.mipt.npm.controls.api.PropertyChangedMessage -import ru.mipt.npm.controls.storage.EventStorage +import space.kscience.controls.api.DeviceMessage +import space.kscience.controls.api.PropertyChangedMessage +import space.kscience.controls.storage.EventStorage import ru.mipt.npm.magix.server.GenericMagixMessage import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory From c22902cc915c855374433cdbfd55e03c4106938b Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 2 Aug 2022 09:37:36 +0300 Subject: [PATCH 62/74] Updata to DataForge 0.6 --- build.gradle.kts | 2 +- .../space/kscience/controls/manager/DeviceManager.kt | 2 +- .../kotlin/space/kscience/controls/ports/phrases.kt | 12 ++++++------ .../kotlin/space/kscience/controls/ports/TcpPort.kt | 2 +- .../space/kscience/controls/serial/SerialPort.kt | 2 +- .../controls/xodus/XodusDeviceMessageStorage.kt | 3 +-- .../space/kscience/controls/storage/storageCommon.kt | 4 ++-- .../space/kscience/controls/storage/workDirectory.kt | 2 +- .../space/kscience/controls/ports/KtorTcpPort.kt | 2 +- .../space/kscience/controls/demo/DemoDevice.kt | 2 +- .../kscience/controls/demo/car/MagixVirtualCar.kt | 2 +- .../space/kscience/controls/demo/car/VirtualCar.kt | 2 +- .../devices/pimotionmaster/PiMotionMasterDevice.kt | 2 +- 13 files changed, 19 insertions(+), 20 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 48be680..75b36df 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("ru.mipt.npm.gradle.project") } -val dataforgeVersion: String by extra("0.5.2") +val dataforgeVersion: String by extra("0.6.0-dev-12") val ktorVersion: String by extra(ru.mipt.npm.gradle.KScienceVersions.ktorVersion) val rsocketVersion by extra("0.15.4") diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/manager/DeviceManager.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/manager/DeviceManager.kt index 2e7d186..a3a5bfd 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/manager/DeviceManager.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/manager/DeviceManager.kt @@ -31,7 +31,7 @@ public class DeviceManager : AbstractPlugin(), DeviceHub { override val tag: PluginTag = PluginTag("devices", group = PluginTag.DATAFORGE_GROUP) override val type: KClass = DeviceManager::class - override fun invoke(meta: Meta, context: Context): DeviceManager = DeviceManager() + override fun build(context: Context, meta: Meta): DeviceManager = DeviceManager() } } diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt index 896d87a..8543825 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt @@ -10,10 +10,10 @@ import kotlinx.coroutines.flow.transform /** * Transform byte fragments into complete phrases using given delimiter. Not thread safe. */ -public fun Flow.withDelimiter(delimiter: ByteArray, expectedMessageSize: Int = 32): Flow { +public fun Flow.withDelimiter(delimiter: ByteArray): Flow { require(delimiter.isNotEmpty()) { "Delimiter must not be empty" } - val output = BytePacketBuilder(expectedMessageSize) + val output = BytePacketBuilder() var matcherPosition = 0 return transform { chunk -> @@ -40,12 +40,12 @@ public fun Flow.withDelimiter(delimiter: ByteArray, expectedMessageSi /** * Transform byte fragments into utf-8 phrases using utf-8 delimiter */ -public fun Flow.withDelimiter(delimiter: String, expectedMessageSize: Int = 32): Flow { - return withDelimiter(delimiter.encodeToByteArray(), expectedMessageSize).map { it.decodeToString() } +public fun Flow.withDelimiter(delimiter: String): Flow { + return withDelimiter(delimiter.encodeToByteArray()).map { it.decodeToString() } } /** * A flow of delimited phrases */ -public suspend fun Port.delimitedIncoming(delimiter: ByteArray, expectedMessageSize: Int = 32): Flow = - receiving().withDelimiter(delimiter, expectedMessageSize) +public suspend fun Port.delimitedIncoming(delimiter: ByteArray): Flow = + receiving().withDelimiter(delimiter) diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt index 2ef0d89..d458c4d 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt @@ -83,7 +83,7 @@ public class TcpPort private constructor( return TcpPort(context, host, port, coroutineContext) } - override fun invoke(meta: Meta, context: Context): Port { + override fun build(context: Context, meta: Meta): Port { val host = meta["host"].string ?: "localhost" val port = meta["port"].int ?: error("Port value for TCP port is not defined in $meta") return open(context, host, port) diff --git a/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt index eab88b6..737e90d 100644 --- a/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt +++ b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt @@ -77,7 +77,7 @@ public class SerialPort private constructor( return SerialPort(context, jssc, coroutineContext) } - override fun invoke(meta: Meta, context: Context): Port { + override fun build(context: Context, meta: Meta): Port { val name by meta.string { error("Serial port name not defined") } val baudRate by meta.int(BAUDRATE_9600) val dataBits by meta.int(DATABITS_8) diff --git a/controls-storage/controls-xodus/src/main/kotlin/space/kscience/controls/xodus/XodusDeviceMessageStorage.kt b/controls-storage/controls-xodus/src/main/kotlin/space/kscience/controls/xodus/XodusDeviceMessageStorage.kt index 2255d89..72cdd1f 100644 --- a/controls-storage/controls-xodus/src/main/kotlin/space/kscience/controls/xodus/XodusDeviceMessageStorage.kt +++ b/controls-storage/controls-xodus/src/main/kotlin/space/kscience/controls/xodus/XodusDeviceMessageStorage.kt @@ -111,8 +111,7 @@ public class XodusDeviceMessageStorage( internal const val DEVICE_MESSAGE_ENTITY_TYPE = "controls-kt.message" public val XODUS_STORE_PROPERTY: Name = Name.of("xodus", "storagePath") - - override fun invoke(meta: Meta, context: Context): XodusDeviceMessageStorage { + override fun build(context: Context, meta: Meta): XodusDeviceMessageStorage { val io = context.fetch(IOPlugin) val storePath = io.workDirectory.resolve( meta[XODUS_STORE_PROPERTY]?.string diff --git a/controls-storage/src/commonMain/kotlin/space/kscience/controls/storage/storageCommon.kt b/controls-storage/src/commonMain/kotlin/space/kscience/controls/storage/storageCommon.kt index 672e2cc..2a453cf 100644 --- a/controls-storage/src/commonMain/kotlin/space/kscience/controls/storage/storageCommon.kt +++ b/controls-storage/src/commonMain/kotlin/space/kscience/controls/storage/storageCommon.kt @@ -15,7 +15,7 @@ import space.kscience.dataforge.context.logger //TODO replace by plugin? public fun DeviceManager.storage( factory: Factory, -): DeviceMessageStorage = factory(meta, context) +): DeviceMessageStorage = factory.build(context, meta) /** * Begin to store DeviceMessages from this DeviceManager @@ -28,7 +28,7 @@ public fun DeviceManager.storeMessages( factory: Factory, filterCondition: suspend (DeviceMessage) -> Boolean = { true }, ): Job { - val storage = factory(meta, context) + val storage = factory.build(context, meta) logger.debug { "Message storage with meta = $meta created" } return hubMessageFlow(context).filter(filterCondition).onEach { message -> diff --git a/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt b/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt index 7e4086f..62ae391 100644 --- a/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt +++ b/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt @@ -3,8 +3,8 @@ package space.kscience.controls.storage import space.kscience.dataforge.context.ContextBuilder import space.kscience.dataforge.io.IOPlugin import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.set import space.kscience.dataforge.meta.string +import space.kscience.dataforge.values.set import java.nio.file.Path import kotlin.io.path.Path diff --git a/controls-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt b/controls-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt index 2c77aa5..9f652b2 100644 --- a/controls-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt +++ b/controls-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt @@ -65,7 +65,7 @@ public class KtorTcpPort internal constructor( return KtorTcpPort(context, host, port, coroutineContext) } - override fun invoke(meta: Meta, context: Context): Port { + override fun build(context: Context, meta: Meta): Port { val host = meta["host"].string ?: "localhost" val port = meta["port"].int ?: error("Port value for TCP port is not defined in $meta") return open(context, host, port) diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt index 26ac20b..4abad84 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt @@ -35,7 +35,7 @@ class DemoDevice(context: Context, meta: Meta) : DeviceBySpec(DemoDe companion object : DeviceSpec(), Factory { - override fun invoke(meta: Meta, context: Context): DemoDevice = DemoDevice(context, meta) + override fun build(context: Context, meta: Meta): DemoDevice = DemoDevice(context, meta) // register virtual properties based on actual object state val timeScale by mutableProperty(MetaConverter.double, DemoDevice::timeScaleState) { diff --git a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt index 48eaa15..b1b57e5 100644 --- a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt @@ -43,6 +43,6 @@ class MagixVirtualCar(context: Context, meta: Meta) : VirtualCar(context, meta) } companion object : Factory { - override fun invoke(meta: Meta, context: Context): MagixVirtualCar = MagixVirtualCar(context, meta) + override fun build(context: Context, meta: Meta): MagixVirtualCar = MagixVirtualCar(context, meta) } } diff --git a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCar.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCar.kt index a1240a4..db0638c 100644 --- a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCar.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCar.kt @@ -110,6 +110,6 @@ open class VirtualCar(context: Context, meta: Meta) : DeviceBySpec(I } companion object : Factory { - override fun invoke(meta: Meta, context: Context): VirtualCar = VirtualCar(context, meta) + override fun build(context: Context, meta: Meta): VirtualCar = VirtualCar(context, meta) } } diff --git a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt index ff35345..9928e45 100644 --- a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt +++ b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt @@ -135,7 +135,7 @@ class PiMotionMasterDevice( companion object : DeviceSpec(), Factory { - override fun invoke(meta: Meta, context: Context): PiMotionMasterDevice = PiMotionMasterDevice(context) + override fun build(context: Context, meta: Meta): PiMotionMasterDevice = PiMotionMasterDevice(context) val connected by booleanProperty(descriptorBuilder = { info = "True if the connection address is defined and the device is initialized" From 4a09e66c42bb0f33edcee321c895cfed41fd8cd4 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 2 Aug 2022 09:46:31 +0300 Subject: [PATCH 63/74] continue package refactor --- build.gradle.kts | 4 ++-- controls-core/build.gradle.kts | 4 ++-- controls-magix-client/build.gradle.kts | 2 +- .../space/kscience/controls/client/controlsMagix.kt | 2 +- .../space/kscience/controls/client/tangoMagix.kt | 2 +- controls-opcua/build.gradle.kts | 4 ++-- controls-serial/build.gradle.kts | 2 +- controls-server/build.gradle.kts | 2 +- .../kscience/controls/server/deviceWebServer.kt | 8 ++++---- .../space/kscience/controls/server/responses.kt | 2 +- controls-storage/build.gradle.kts | 4 ++-- controls-storage/controls-xodus/build.gradle.kts | 4 ++-- controls-tcp/build.gradle.kts | 2 +- .../kscience/controls/demo/DemoControllerView.kt | 8 ++++---- .../space/kscience/controls/demo/demoDeviceServer.kt | 4 ++-- .../kscience/controls/demo/car/MagixVirtualCar.kt | 6 +++--- .../controls/demo/car/VirtualCarController.kt | 8 ++++---- .../kotlin/space/kscience/controls/demo/echo/main.kt | 10 +++++----- demo/magix-demo/build.gradle.kts | 2 +- demo/magix-demo/src/main/kotlin/zmq.kt | 8 ++++---- demo/motors/build.gradle.kts | 4 ++-- gradle.properties | 2 +- magix/magix-api/build.gradle.kts | 4 ++-- .../kscience}/magix/api/MagixEndpoint.kt | 2 +- .../npm => space/kscience}/magix/api/MagixFormat.kt | 4 ++-- .../npm => space/kscience}/magix/api/MagixMessage.kt | 2 +- .../kscience}/magix/api/MagixMessageFilter.kt | 2 +- .../kscience}/magix/api/MagixRegistry.kt | 2 +- .../npm => space/kscience}/magix/api/converters.kt | 2 +- magix/magix-java-client/build.gradle.kts | 4 ++-- .../kscience}/magix/client/MagixClient.java | 4 ++-- .../kscience}/magix/client/ControlsMagixClient.kt | 12 ++++++------ magix/magix-rabbit/build.gradle.kts | 4 ++-- .../kscience}/magix/rabbit/RabbitMQMagixEndpoint.kt | 12 ++++++------ magix/magix-rsocket/build.gradle.kts | 4 ++-- .../kscience}/magix/rsocket/RSocketMagixEndpoint.kt | 10 +++++----- .../magix/rsocket/RSocketStreamMagixEndpoint.kt | 10 +++++----- .../npm => space/kscience}/magix/rsocket/withTcp.kt | 4 ++-- .../src/linuxX64Main/kotlin/rsocket/withTcp.kt | 6 +++--- magix/magix-server/build.gradle.kts | 4 ++-- .../kscience}/magix/server/magixModule.kt | 10 +++++----- .../npm => space/kscience}/magix/server/server.kt | 10 +++++----- .../mipt/npm => space/kscience}/magix/server/sse.kt | 2 +- .../kscience}/magix/server/zmqMagixServerSocket.kt | 6 +++--- .../magix-storage-mongo/build.gradle.kts | 4 ++-- .../magix-storage-xodus/build.gradle.kts | 4 ++-- .../magix/storage/xodus/XodusMagixStorage.kt | 10 +++++----- magix/magix-zmq/build.gradle.kts | 2 +- .../kscince}/magix/zmq/ZmqMagixEndpoint.kt | 10 +++++----- settings.gradle.kts | 10 +++++----- 50 files changed, 127 insertions(+), 127 deletions(-) rename magix/magix-api/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/magix/api/MagixEndpoint.kt (97%) rename magix/magix-api/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/magix/api/MagixFormat.kt (92%) rename magix/magix-api/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/magix/api/MagixMessage.kt (96%) rename magix/magix-api/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/magix/api/MagixMessageFilter.kt (96%) rename magix/magix-api/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/magix/api/MagixRegistry.kt (94%) rename magix/magix-api/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/magix/api/converters.kt (96%) rename magix/magix-java-client/src/main/java/{ru/mipt/npm => space/kscience}/magix/client/MagixClient.java (92%) rename magix/magix-java-client/src/main/kotlin/{ru/mipt/npm => space/kscience}/magix/client/ControlsMagixClient.kt (80%) rename magix/magix-rabbit/src/main/kotlin/{ru/mipt/npm => space/kscience}/magix/rabbit/RabbitMQMagixEndpoint.kt (87%) rename magix/magix-rsocket/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/magix/rsocket/RSocketMagixEndpoint.kt (92%) rename magix/magix-rsocket/src/commonMain/kotlin/{ru/mipt/npm => space/kscience}/magix/rsocket/RSocketStreamMagixEndpoint.kt (93%) rename magix/magix-rsocket/src/jvmMain/kotlin/{ru/mipt/npm => space/kscience}/magix/rsocket/withTcp.kt (94%) rename magix/magix-server/src/main/kotlin/{ru/mipt/npm => space/kscience}/magix/server/magixModule.kt (95%) rename magix/magix-server/src/main/kotlin/{ru/mipt/npm => space/kscience}/magix/server/server.kt (90%) rename magix/magix-server/src/main/kotlin/{ru/mipt/npm => space/kscience}/magix/server/sse.kt (96%) rename magix/magix-server/src/main/kotlin/{ru/mipt/npm => space/kscience}/magix/server/zmqMagixServerSocket.kt (92%) rename magix/magix-storage/magix-storage-xodus/src/main/kotlin/{ru/mipt/npm => space/kscience}/magix/storage/xodus/XodusMagixStorage.kt (93%) rename magix/magix-zmq/src/main/kotlin/{ru/mipt/npm => space/kscince}/magix/zmq/ZmqMagixEndpoint.kt (92%) diff --git a/build.gradle.kts b/build.gradle.kts index 75b36df..ceec553 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,9 @@ plugins { - id("ru.mipt.npm.gradle.project") + id("space.kscience.gradle.project") } val dataforgeVersion: String by extra("0.6.0-dev-12") -val ktorVersion: String by extra(ru.mipt.npm.gradle.KScienceVersions.ktorVersion) +val ktorVersion: String by extra(space.kscience.gradle.KScienceVersions.ktorVersion) val rsocketVersion by extra("0.15.4") allprojects { diff --git a/controls-core/build.gradle.kts b/controls-core/build.gradle.kts index 777cb1e..159e693 100644 --- a/controls-core/build.gradle.kts +++ b/controls-core/build.gradle.kts @@ -1,6 +1,6 @@ plugins { - id("ru.mipt.npm.gradle.mpp") - id("ru.mipt.npm.gradle.native") + id("space.kscience.gradle.mpp") + id("space.kscience.gradle.native") `maven-publish` } diff --git a/controls-magix-client/build.gradle.kts b/controls-magix-client/build.gradle.kts index 7941c2e..2105e37 100644 --- a/controls-magix-client/build.gradle.kts +++ b/controls-magix-client/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.mpp") + id("space.kscience.gradle.mpp") `maven-publish` } diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt index 7355df0..be77a4a 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt @@ -5,13 +5,13 @@ import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import ru.mipt.npm.magix.api.* import space.kscience.controls.api.DeviceMessage import space.kscience.controls.manager.DeviceManager import space.kscience.controls.manager.hubMessageFlow import space.kscience.controls.manager.respondHubMessage import space.kscience.dataforge.context.error import space.kscience.dataforge.context.logger +import space.kscience.magix.api.* public val controlsMagixFormat: MagixFormat = MagixFormat( diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt index 1638d85..ddec8d6 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt @@ -5,13 +5,13 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.serialization.Serializable -import ru.mipt.npm.magix.api.* import space.kscience.controls.api.get import space.kscience.controls.api.getOrReadProperty import space.kscience.controls.manager.DeviceManager import space.kscience.dataforge.context.error import space.kscience.dataforge.context.logger import space.kscience.dataforge.meta.Meta +import space.kscience.magix.api.* public const val TANGO_MAGIX_FORMAT: String = "tango" diff --git a/controls-opcua/build.gradle.kts b/controls-opcua/build.gradle.kts index a2a225d..c5894e4 100644 --- a/controls-opcua/build.gradle.kts +++ b/controls-opcua/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") } val ktorVersion: String by rootProject.extra @@ -8,7 +8,7 @@ val miloVersion: String = "0.6.3" dependencies { api(project(":controls-core")) - api("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:${ru.mipt.npm.gradle.KScienceVersions.coroutinesVersion}") + api("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:${space.kscience.gradle.KScienceVersions.coroutinesVersion}") api("org.eclipse.milo:sdk-client:$miloVersion") api("org.eclipse.milo:bsd-parser:$miloVersion") diff --git a/controls-serial/build.gradle.kts b/controls-serial/build.gradle.kts index 733348c..b28a210 100644 --- a/controls-serial/build.gradle.kts +++ b/controls-serial/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") `maven-publish` } diff --git a/controls-server/build.gradle.kts b/controls-server/build.gradle.kts index 710550c..e9471a5 100644 --- a/controls-server/build.gradle.kts +++ b/controls-server/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") `maven-publish` } diff --git a/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt b/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt index 881ad1b..434aaa0 100644 --- a/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt +++ b/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt @@ -26,10 +26,6 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.buildJsonArray import kotlinx.serialization.json.encodeToJsonElement import kotlinx.serialization.json.put -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixMessage -import ru.mipt.npm.magix.server.launchMagixServerRawRSocket -import ru.mipt.npm.magix.server.magixModule import space.kscience.controls.api.DeviceMessage import space.kscience.controls.api.PropertyGetMessage import space.kscience.controls.api.PropertySetMessage @@ -39,6 +35,10 @@ import space.kscience.controls.manager.respondHubMessage import space.kscience.dataforge.meta.toMeta import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.server.launchMagixServerRawRSocket +import space.kscience.magix.server.magixModule /** * Create and start a web server for several devices diff --git a/controls-server/src/main/kotlin/space/kscience/controls/server/responses.kt b/controls-server/src/main/kotlin/space/kscience/controls/server/responses.kt index 96912e2..93d11c4 100644 --- a/controls-server/src/main/kotlin/space/kscience/controls/server/responses.kt +++ b/controls-server/src/main/kotlin/space/kscience/controls/server/responses.kt @@ -5,8 +5,8 @@ import io.ktor.server.application.ApplicationCall import io.ktor.server.response.respondText import kotlinx.serialization.json.JsonObjectBuilder import kotlinx.serialization.json.buildJsonObject -import ru.mipt.npm.magix.api.MagixEndpoint import space.kscience.controls.api.DeviceMessage +import space.kscience.magix.api.MagixEndpoint //internal fun Frame.toEnvelope(): Envelope { diff --git a/controls-storage/build.gradle.kts b/controls-storage/build.gradle.kts index ab25e1d..80210e8 100644 --- a/controls-storage/build.gradle.kts +++ b/controls-storage/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.mpp") + id("space.kscience.gradle.mpp") `maven-publish` } @@ -24,5 +24,5 @@ kotlin { } readme{ - maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE + maturity = space.kscience.gradle.Maturity.PROTOTYPE } diff --git a/controls-storage/controls-xodus/build.gradle.kts b/controls-storage/controls-xodus/build.gradle.kts index ae99038..a912825 100644 --- a/controls-storage/controls-xodus/build.gradle.kts +++ b/controls-storage/controls-xodus/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") `maven-publish` } @@ -15,5 +15,5 @@ dependencies { } readme{ - maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE + maturity = space.kscience.gradle.Maturity.PROTOTYPE } diff --git a/controls-tcp/build.gradle.kts b/controls-tcp/build.gradle.kts index 4ed2905..c15c892 100644 --- a/controls-tcp/build.gradle.kts +++ b/controls-tcp/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") } val ktorVersion: String by rootProject.extra diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt index a37430f..4decac5 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt @@ -8,10 +8,6 @@ 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.magix.api.MagixEndpoint -import ru.mipt.npm.magix.rsocket.rSocketWithTcp -import ru.mipt.npm.magix.rsocket.rSocketWithWebSockets -import ru.mipt.npm.magix.server.startMagixServer import space.kscience.controls.client.connectToMagix import space.kscience.controls.demo.DemoDevice.Companion.cosScale import space.kscience.controls.demo.DemoDevice.Companion.sinScale @@ -22,6 +18,10 @@ import space.kscience.controls.opcua.server.OpcUaServer import space.kscience.controls.opcua.server.endpoint import space.kscience.controls.opcua.server.serveDevices import space.kscience.dataforge.context.* +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.rsocket.rSocketWithTcp +import space.kscience.magix.rsocket.rSocketWithWebSockets +import space.kscience.magix.server.startMagixServer import tornadofx.* import java.awt.Desktop import java.net.URI diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt index 29791ae..1903416 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt @@ -11,12 +11,12 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.html.div import kotlinx.html.link -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.subscribe import space.kscience.controls.api.PropertyChangedMessage import space.kscience.controls.client.controlsMagixFormat import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.double +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.subscribe import space.kscience.plotly.layout import space.kscience.plotly.models.Trace import space.kscience.plotly.plot diff --git a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt index b1b57e5..5cf3386 100644 --- a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt @@ -1,9 +1,6 @@ package space.kscience.controls.demo.car import kotlinx.coroutines.launch -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.subscribe -import ru.mipt.npm.magix.rsocket.rSocketWithWebSockets import space.kscience.controls.api.PropertyChangedMessage import space.kscience.controls.client.controlsMagixFormat import space.kscience.dataforge.context.Context @@ -12,6 +9,9 @@ import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string import space.kscience.dataforge.names.Name +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.subscribe +import space.kscience.magix.rsocket.rSocketWithWebSockets import kotlin.time.ExperimentalTime class MagixVirtualCar(context: Context, meta: Meta) : VirtualCar(context, meta) { diff --git a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt index 143969e..5e6651e 100644 --- a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt @@ -8,10 +8,6 @@ import javafx.scene.layout.Priority import javafx.stage.Stage import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.rsocket.rSocketWithTcp -import ru.mipt.npm.magix.server.startMagixServer -import ru.mipt.npm.magix.storage.xodus.storeInXodus import space.kscience.controls.client.connectToMagix import space.kscience.controls.demo.car.IVirtualCar.Companion.acceleration import space.kscience.controls.manager.DeviceManager @@ -20,6 +16,10 @@ import space.kscience.controls.storage.storeMessages import space.kscience.controls.xodus.XodusDeviceMessageStorage import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.rsocket.rSocketWithTcp +import space.kscience.magix.server.startMagixServer +import space.kscience.magix.storage.xodus.storeInXodus import tornadofx.* import java.nio.file.Paths diff --git a/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt b/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt index 05dab38..a02c044 100644 --- a/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt +++ b/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt @@ -5,11 +5,11 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.serialization.json.JsonObject -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixMessage -import ru.mipt.npm.magix.api.MagixMessageFilter -import ru.mipt.npm.magix.rsocket.rSocketStreamWithWebSockets -import ru.mipt.npm.magix.server.startMagixServer +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter +import space.kscience.magix.rsocket.rSocketStreamWithWebSockets +import space.kscience.magix.server.startMagixServer import kotlin.time.ExperimentalTime import kotlin.time.measureTime diff --git a/demo/magix-demo/build.gradle.kts b/demo/magix-demo/build.gradle.kts index 0b6e72b..411b9c4 100644 --- a/demo/magix-demo/build.gradle.kts +++ b/demo/magix-demo/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") application } diff --git a/demo/magix-demo/src/main/kotlin/zmq.kt b/demo/magix-demo/src/main/kotlin/zmq.kt index c8c4b45..6a7e01d 100644 --- a/demo/magix-demo/src/main/kotlin/zmq.kt +++ b/demo/magix-demo/src/main/kotlin/zmq.kt @@ -7,10 +7,10 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.isActive import kotlinx.serialization.json.* import org.slf4j.LoggerFactory -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixMessage -import ru.mipt.npm.magix.server.startMagixServer -import ru.mipt.npm.magix.zmq.ZmqMagixEndpoint +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.server.startMagixServer +import space.kscince.magix.zmq.ZmqMagixEndpoint import java.awt.Desktop import java.net.URI diff --git a/demo/motors/build.gradle.kts b/demo/motors/build.gradle.kts index 503ab5d..1427557 100644 --- a/demo/motors/build.gradle.kts +++ b/demo/motors/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") `maven-publish` application } @@ -15,7 +15,7 @@ kotlin{ } kscience{ - useFx(ru.mipt.npm.gradle.FXModule.CONTROLS, configuration = ru.mipt.npm.gradle.DependencyConfiguration.IMPLEMENTATION) + useFx(space.kscience.gradle.FXModule.CONTROLS, configuration = space.kscience.gradle.DependencyConfiguration.IMPLEMENTATION) } val ktorVersion: String by rootProject.extra diff --git a/gradle.properties b/gradle.properties index 1d1dcc7..52d10eb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,4 +7,4 @@ org.gradle.parallel=true publishing.github=false publishing.sonatype=false -toolsVersion=0.11.7-kotlin-1.7.0 \ No newline at end of file +toolsVersion=0.12.0-kotlin-1.7.20-Beta \ No newline at end of file diff --git a/magix/magix-api/build.gradle.kts b/magix/magix-api/build.gradle.kts index 7a11b09..0a23e4c 100644 --- a/magix/magix-api/build.gradle.kts +++ b/magix/magix-api/build.gradle.kts @@ -1,6 +1,6 @@ plugins { - id("ru.mipt.npm.gradle.mpp") - id("ru.mipt.npm.gradle.native") + id("space.kscience.gradle.mpp") + id("space.kscience.gradle.native") `maven-publish` } diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixEndpoint.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt similarity index 97% rename from magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixEndpoint.kt rename to magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt index 52d591f..9baafea 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixEndpoint.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.api +package space.kscience.magix.api import kotlinx.coroutines.flow.Flow import kotlinx.serialization.json.Json diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixFormat.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt similarity index 92% rename from magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixFormat.kt rename to magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt index a5df654..4921129 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixFormat.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt @@ -1,10 +1,10 @@ -package ru.mipt.npm.magix.api +package space.kscience.magix.api import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import kotlinx.serialization.KSerializer import kotlinx.serialization.json.JsonElement -import ru.mipt.npm.magix.api.MagixEndpoint.Companion.magixJson +import space.kscience.magix.api.MagixEndpoint.Companion.magixJson public data class MagixFormat( val serializer: KSerializer, diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessage.kt similarity index 96% rename from magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt rename to magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessage.kt index 44e7d6a..c25c08a 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessage.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessage.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.api +package space.kscience.magix.api import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessageFilter.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt similarity index 96% rename from magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessageFilter.kt rename to magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt index fb6ab4e..f497b97 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixMessageFilter.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.api +package space.kscience.magix.api import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixRegistry.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixRegistry.kt similarity index 94% rename from magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixRegistry.kt rename to magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixRegistry.kt index a13b9ce..7b3b111 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/MagixRegistry.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixRegistry.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.api +package space.kscience.magix.api import kotlinx.serialization.json.JsonElement diff --git a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/converters.kt similarity index 96% rename from magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt rename to magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/converters.kt index 6c558ad..441e141 100644 --- a/magix/magix-api/src/commonMain/kotlin/ru/mipt/npm/magix/api/converters.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/converters.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.api +package space.kscience.magix.api import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job diff --git a/magix/magix-java-client/build.gradle.kts b/magix/magix-java-client/build.gradle.kts index d250b3f..7a14d44 100644 --- a/magix/magix-java-client/build.gradle.kts +++ b/magix/magix-java-client/build.gradle.kts @@ -1,12 +1,12 @@ plugins { java - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") `maven-publish` } dependencies { implementation(project(":magix:magix-rsocket")) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:${ru.mipt.npm.gradle.KScienceVersions.coroutinesVersion}") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:${space.kscience.gradle.KScienceVersions.coroutinesVersion}") } java { diff --git a/magix/magix-java-client/src/main/java/ru/mipt/npm/magix/client/MagixClient.java b/magix/magix-java-client/src/main/java/space/kscience/magix/client/MagixClient.java similarity index 92% rename from magix/magix-java-client/src/main/java/ru/mipt/npm/magix/client/MagixClient.java rename to magix/magix-java-client/src/main/java/space/kscience/magix/client/MagixClient.java index 3da2364..3270544 100644 --- a/magix/magix-java-client/src/main/java/ru/mipt/npm/magix/client/MagixClient.java +++ b/magix/magix-java-client/src/main/java/space/kscience/magix/client/MagixClient.java @@ -1,7 +1,7 @@ -package ru.mipt.npm.magix.client; +package space.kscience.magix.client; import kotlinx.serialization.json.JsonElement; -import ru.mipt.npm.magix.api.MagixMessage; +import space.kscience.magix.api.MagixMessage; import java.io.IOException; import java.util.concurrent.Flow; diff --git a/magix/magix-java-client/src/main/kotlin/ru/mipt/npm/magix/client/ControlsMagixClient.kt b/magix/magix-java-client/src/main/kotlin/space/kscience/magix/client/ControlsMagixClient.kt similarity index 80% rename from magix/magix-java-client/src/main/kotlin/ru/mipt/npm/magix/client/ControlsMagixClient.kt rename to magix/magix-java-client/src/main/kotlin/space/kscience/magix/client/ControlsMagixClient.kt index 4abb15f..1efa746 100644 --- a/magix/magix-java-client/src/main/kotlin/ru/mipt/npm/magix/client/ControlsMagixClient.kt +++ b/magix/magix-java-client/src/main/kotlin/space/kscience/magix/client/ControlsMagixClient.kt @@ -1,12 +1,12 @@ -package ru.mipt.npm.magix.client +package space.kscience.magix.client import kotlinx.coroutines.jdk9.asPublisher import kotlinx.coroutines.runBlocking -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixMessage -import ru.mipt.npm.magix.api.MagixMessageFilter -import ru.mipt.npm.magix.rsocket.rSocketWithTcp -import ru.mipt.npm.magix.rsocket.rSocketWithWebSockets +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter +import space.kscience.magix.rsocket.rSocketWithTcp +import space.kscience.magix.rsocket.rSocketWithWebSockets import java.util.concurrent.Flow internal class ControlsMagixClient( diff --git a/magix/magix-rabbit/build.gradle.kts b/magix/magix-rabbit/build.gradle.kts index 960a121..3d7bc4d 100644 --- a/magix/magix-rabbit/build.gradle.kts +++ b/magix/magix-rabbit/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") `maven-publish` } @@ -13,5 +13,5 @@ dependencies { } readme{ - maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE + maturity = space.kscience.gradle.Maturity.PROTOTYPE } diff --git a/magix/magix-rabbit/src/main/kotlin/ru/mipt/npm/magix/rabbit/RabbitMQMagixEndpoint.kt b/magix/magix-rabbit/src/main/kotlin/space/kscience/magix/rabbit/RabbitMQMagixEndpoint.kt similarity index 87% rename from magix/magix-rabbit/src/main/kotlin/ru/mipt/npm/magix/rabbit/RabbitMQMagixEndpoint.kt rename to magix/magix-rabbit/src/main/kotlin/space/kscience/magix/rabbit/RabbitMQMagixEndpoint.kt index a709df8..33af457 100644 --- a/magix/magix-rabbit/src/main/kotlin/ru/mipt/npm/magix/rabbit/RabbitMQMagixEndpoint.kt +++ b/magix/magix-rabbit/src/main/kotlin/space/kscience/magix/rabbit/RabbitMQMagixEndpoint.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.rabbit +package space.kscience.magix.rabbit import com.rabbitmq.client.* import kotlinx.coroutines.cancel @@ -6,11 +6,11 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.launch -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixMessage -import ru.mipt.npm.magix.api.MagixMessageFilter -import ru.mipt.npm.magix.api.filter -import ru.mipt.npm.magix.rabbit.RabbitMQMagixEndpoint.Companion.DEFAULT_MAGIX_QUEUE_NAME +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter +import space.kscience.magix.api.filter +import space.kscience.magix.rabbit.RabbitMQMagixEndpoint.Companion.DEFAULT_MAGIX_QUEUE_NAME /** * A magix endpoint for RabbitMQ message broker diff --git a/magix/magix-rsocket/build.gradle.kts b/magix/magix-rsocket/build.gradle.kts index b5c1eb8..7f637b3 100644 --- a/magix/magix-rsocket/build.gradle.kts +++ b/magix/magix-rsocket/build.gradle.kts @@ -1,6 +1,6 @@ plugins { - id("ru.mipt.npm.gradle.mpp") - id("ru.mipt.npm.gradle.native") + id("space.kscience.gradle.mpp") + id("space.kscience.gradle.native") `maven-publish` } diff --git a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt b/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketMagixEndpoint.kt similarity index 92% rename from magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt rename to magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketMagixEndpoint.kt index cb165cf..43c505c 100644 --- a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketMagixEndpoint.kt +++ b/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketMagixEndpoint.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.rsocket +package space.kscience.magix.rsocket import io.ktor.client.HttpClient import io.ktor.client.plugins.websocket.WebSockets @@ -14,10 +14,10 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixMessage -import ru.mipt.npm.magix.api.MagixMessageFilter -import ru.mipt.npm.magix.api.filter +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter +import space.kscience.magix.api.filter import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext diff --git a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketStreamMagixEndpoint.kt b/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketStreamMagixEndpoint.kt similarity index 93% rename from magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketStreamMagixEndpoint.kt rename to magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketStreamMagixEndpoint.kt index 25f99bb..1875602 100644 --- a/magix/magix-rsocket/src/commonMain/kotlin/ru/mipt/npm/magix/rsocket/RSocketStreamMagixEndpoint.kt +++ b/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketStreamMagixEndpoint.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.rsocket +package space.kscience.magix.rsocket import io.ktor.client.HttpClient import io.ktor.client.plugins.websocket.WebSockets @@ -18,10 +18,10 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixMessage -import ru.mipt.npm.magix.api.MagixMessageFilter -import ru.mipt.npm.magix.api.filter +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter +import space.kscience.magix.api.filter import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext diff --git a/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt b/magix/magix-rsocket/src/jvmMain/kotlin/space/kscience/magix/rsocket/withTcp.kt similarity index 94% rename from magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt rename to magix/magix-rsocket/src/jvmMain/kotlin/space/kscience/magix/rsocket/withTcp.kt index 5d76532..23fe0be 100644 --- a/magix/magix-rsocket/src/jvmMain/kotlin/ru/mipt/npm/magix/rsocket/withTcp.kt +++ b/magix/magix-rsocket/src/jvmMain/kotlin/space/kscience/magix/rsocket/withTcp.kt @@ -1,9 +1,9 @@ -package ru.mipt.npm.magix.rsocket +package space.kscience.magix.rsocket import io.ktor.network.sockets.SocketOptions import io.rsocket.kotlin.core.RSocketConnectorBuilder import io.rsocket.kotlin.transport.ktor.tcp.TcpClientTransport -import ru.mipt.npm.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixEndpoint import kotlin.coroutines.coroutineContext diff --git a/magix/magix-rsocket/src/linuxX64Main/kotlin/rsocket/withTcp.kt b/magix/magix-rsocket/src/linuxX64Main/kotlin/rsocket/withTcp.kt index 8e1d596..117bcdd 100644 --- a/magix/magix-rsocket/src/linuxX64Main/kotlin/rsocket/withTcp.kt +++ b/magix/magix-rsocket/src/linuxX64Main/kotlin/rsocket/withTcp.kt @@ -3,9 +3,9 @@ package rsocket import io.ktor.network.sockets.SocketOptions import io.rsocket.kotlin.core.RSocketConnectorBuilder import io.rsocket.kotlin.transport.ktor.tcp.TcpClientTransport -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.rsocket.RSocketMagixEndpoint -import ru.mipt.npm.magix.rsocket.buildConnector +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.rsocket.RSocketMagixEndpoint +import space.kscience.magix.rsocket.buildConnector import kotlin.coroutines.coroutineContext diff --git a/magix/magix-server/build.gradle.kts b/magix/magix-server/build.gradle.kts index f73528d..dc7cb28 100644 --- a/magix/magix-server/build.gradle.kts +++ b/magix/magix-server/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") `maven-publish` application } @@ -16,7 +16,7 @@ kscience { val dataforgeVersion: String by rootProject.extra val rsocketVersion: String by rootProject.extra -val ktorVersion: String = ru.mipt.npm.gradle.KScienceVersions.ktorVersion +val ktorVersion: String = space.kscience.gradle.KScienceVersions.ktorVersion dependencies{ api(projects.magix.magixApi) diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt similarity index 95% rename from magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt rename to magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt index 6cbf00a..50c5921 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/magixModule.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.server +package space.kscience.magix.server import io.ktor.serialization.kotlinx.json.json import io.ktor.server.application.* @@ -19,10 +19,10 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* import kotlinx.html.* import kotlinx.serialization.encodeToString -import ru.mipt.npm.magix.api.MagixEndpoint.Companion.magixJson -import ru.mipt.npm.magix.api.MagixMessage -import ru.mipt.npm.magix.api.MagixMessageFilter -import ru.mipt.npm.magix.api.filter +import space.kscience.magix.api.MagixEndpoint.Companion.magixJson +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter +import space.kscience.magix.api.filter import java.util.* diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/server.kt similarity index 90% rename from magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt rename to magix/magix-server/src/main/kotlin/space/kscience/magix/server/server.kt index e75b594..8ed3ba5 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/server.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/server.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.server +package space.kscience.magix.server import io.ktor.server.application.Application import io.ktor.server.cio.CIO @@ -12,10 +12,10 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import org.slf4j.LoggerFactory -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_HTTP_PORT -import ru.mipt.npm.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_RAW_PORT -import ru.mipt.npm.magix.api.MagixMessage +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_HTTP_PORT +import space.kscience.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_RAW_PORT +import space.kscience.magix.api.MagixMessage /** * Raw TCP magix server diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/sse.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/sse.kt similarity index 96% rename from magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/sse.kt rename to magix/magix-server/src/main/kotlin/space/kscience/magix/server/sse.kt index c771ab0..21db0b9 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/sse.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/sse.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.server +package space.kscience.magix.server import io.ktor.http.CacheControl import io.ktor.http.ContentType diff --git a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/zmqMagixServerSocket.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/zmqMagixServerSocket.kt similarity index 92% rename from magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/zmqMagixServerSocket.kt rename to magix/magix-server/src/main/kotlin/space/kscience/magix/server/zmqMagixServerSocket.kt index f0c62b8..18717e4 100644 --- a/magix/magix-server/src/main/kotlin/ru/mipt/npm/magix/server/zmqMagixServerSocket.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/zmqMagixServerSocket.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.server +package space.kscience.magix.server import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableSharedFlow @@ -9,8 +9,8 @@ import kotlinx.serialization.encodeToString import org.slf4j.LoggerFactory import org.zeromq.SocketType import org.zeromq.ZContext -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixMessage +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixMessage public fun CoroutineScope.launchMagixServerZmqSocket( magixFlow: MutableSharedFlow, diff --git a/magix/magix-storage/magix-storage-mongo/build.gradle.kts b/magix/magix-storage/magix-storage-mongo/build.gradle.kts index fa27fb3..51ed9f4 100644 --- a/magix/magix-storage/magix-storage-mongo/build.gradle.kts +++ b/magix/magix-storage/magix-storage-mongo/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") `maven-publish` } @@ -11,5 +11,5 @@ dependencies { } readme{ - maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE + maturity = space.kscience.gradle.Maturity.PROTOTYPE } diff --git a/magix/magix-storage/magix-storage-xodus/build.gradle.kts b/magix/magix-storage/magix-storage-xodus/build.gradle.kts index 2ac7af2..9019cf1 100644 --- a/magix/magix-storage/magix-storage-xodus/build.gradle.kts +++ b/magix/magix-storage/magix-storage-xodus/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") `maven-publish` } @@ -17,5 +17,5 @@ dependencies { } readme{ - maturity = ru.mipt.npm.gradle.Maturity.PROTOTYPE + maturity = space.kscience.gradle.Maturity.PROTOTYPE } diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt similarity index 93% rename from magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt rename to magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt index e14a1af..87e4096 100644 --- a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/ru/mipt/npm/magix/storage/xodus/XodusMagixStorage.kt +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.storage.xodus +package space.kscience.magix.storage.xodus import jetbrains.exodus.entitystore.Entity import jetbrains.exodus.entitystore.PersistentEntityStore @@ -8,10 +8,10 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.serialization.encodeToString import kotlinx.serialization.json.JsonObject -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixEndpoint.Companion.magixJson -import ru.mipt.npm.magix.api.MagixMessage -import ru.mipt.npm.magix.api.MagixMessageFilter +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixEndpoint.Companion.magixJson +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter import java.nio.file.Path public class XodusMagixStorage( diff --git a/magix/magix-zmq/build.gradle.kts b/magix/magix-zmq/build.gradle.kts index cf9e4be..b47b568 100644 --- a/magix/magix-zmq/build.gradle.kts +++ b/magix/magix-zmq/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("ru.mipt.npm.gradle.jvm") + id("space.kscience.gradle.jvm") `maven-publish` } diff --git a/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt similarity index 92% rename from magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt rename to magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt index b621a69..d8c8343 100644 --- a/magix/magix-zmq/src/main/kotlin/ru/mipt/npm/magix/zmq/ZmqMagixEndpoint.kt +++ b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt @@ -1,4 +1,4 @@ -package ru.mipt.npm.magix.zmq +package space.kscince.magix.zmq import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow @@ -8,10 +8,10 @@ import org.zeromq.SocketType import org.zeromq.ZContext import org.zeromq.ZMQ import org.zeromq.ZMQException -import ru.mipt.npm.magix.api.MagixEndpoint -import ru.mipt.npm.magix.api.MagixMessage -import ru.mipt.npm.magix.api.MagixMessageFilter -import ru.mipt.npm.magix.api.filter +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter +import space.kscience.magix.api.filter import kotlin.coroutines.CoroutineContext import kotlin.coroutines.coroutineContext diff --git a/settings.gradle.kts b/settings.gradle.kts index aff8bbd..5b2bf00 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,10 +15,10 @@ pluginManagement { } plugins { - id("ru.mipt.npm.gradle.project") version toolsVersion - id("ru.mipt.npm.gradle.mpp") version toolsVersion - id("ru.mipt.npm.gradle.jvm") version toolsVersion - id("ru.mipt.npm.gradle.js") version toolsVersion + id("space.kscience.gradle.project") version toolsVersion + id("space.kscience.gradle.mpp") version toolsVersion + id("space.kscience.gradle.jvm") version toolsVersion + id("space.kscience.gradle.js") version toolsVersion } } @@ -34,7 +34,7 @@ dependencyResolutionManagement { versionCatalogs { create("npmlibs") { - from("ru.mipt.npm:version-catalog:$toolsVersion") + from("space.kscience:version-catalog:$toolsVersion") } } } From 619c2c280d6cf7e5eb494a69f0171ee738b74945 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 2 Aug 2022 10:12:16 +0300 Subject: [PATCH 64/74] Hotfix for jdkRelease problem --- magix/magix-java-client/build.gradle.kts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/magix/magix-java-client/build.gradle.kts b/magix/magix-java-client/build.gradle.kts index 7a14d44..6a76f23 100644 --- a/magix/magix-java-client/build.gradle.kts +++ b/magix/magix-java-client/build.gradle.kts @@ -1,3 +1,6 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import space.kscience.gradle.KScienceVersions + plugins { java id("space.kscience.gradle.jvm") @@ -6,10 +9,18 @@ plugins { dependencies { implementation(project(":magix:magix-rsocket")) - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:${space.kscience.gradle.KScienceVersions.coroutinesVersion}") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:${KScienceVersions.coroutinesVersion}") } java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = KScienceVersions.JVM_TARGET + targetCompatibility = KScienceVersions.JVM_TARGET +} + + +//FIXME https://youtrack.jetbrains.com/issue/KT-52815/Compiler-option-Xjdk-release-fails-to-compile-mixed-projects +tasks.withType{ + kotlinOptions { + freeCompilerArgs -= "-Xjdk-release=11" + } } From dc6847196df89ff2220eaf2c9e58aa7a26222a31 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 10 Aug 2022 19:20:06 +0300 Subject: [PATCH 65/74] Updated dependencies --- build.gradle.kts | 2 +- .../commonMain/kotlin/space/kscience/controls/spec/misc.kt | 7 +------ .../space/kscience/controls/spec/propertySpecDelegates.kt | 2 +- .../kotlin/space/kscience/controls/misc/javaTimeMeta.kt | 1 - controls-opcua/build.gradle.kts | 2 +- .../space/kscience/controls/opcua/client/MetaBsdParser.kt | 1 - .../kscience/controls/opcua/server/DeviceNameSpace.kt | 2 +- .../space/kscience/controls/opcua/server/metaToOpc.kt | 5 +---- .../space/kscience/controls/storage/workDirectory.kt | 2 +- demo/all-things/build.gradle.kts | 2 +- .../main/kotlin/space/kscience/controls/demo/DemoDevice.kt | 2 +- .../space/kscience/controls/demo/demoDeviceServer.kt | 2 +- .../npm/devices/pimotionmaster/PiMotionMasterDevice.kt | 2 +- magix/magix-java-client/build.gradle.kts | 2 +- 14 files changed, 12 insertions(+), 22 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index ceec553..e35fa92 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ plugins { id("space.kscience.gradle.project") } -val dataforgeVersion: String by extra("0.6.0-dev-12") +val dataforgeVersion: String by extra("0.6.0-dev-13") val ktorVersion: String by extra(space.kscience.gradle.KScienceVersions.ktorVersion) val rsocketVersion by extra("0.15.4") diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/misc.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/misc.kt index dfcc76e..e264212 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/misc.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/misc.kt @@ -1,12 +1,7 @@ package space.kscience.controls.spec -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.double -import space.kscience.dataforge.meta.enum -import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.transformations.MetaConverter -import space.kscience.dataforge.values.asValue -import space.kscience.dataforge.values.double import kotlin.time.Duration import kotlin.time.DurationUnit import kotlin.time.toDuration diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt index 3d701e3..315b942 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt @@ -3,8 +3,8 @@ package space.kscience.controls.spec import space.kscience.controls.api.PropertyDescriptor import space.kscience.controls.api.metaDescriptor import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.ValueType import space.kscience.dataforge.meta.transformations.MetaConverter -import space.kscience.dataforge.values.ValueType import kotlin.properties.PropertyDelegateProvider import kotlin.properties.ReadOnlyProperty diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/misc/javaTimeMeta.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/misc/javaTimeMeta.kt index 30829b3..891c30b 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/misc/javaTimeMeta.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/misc/javaTimeMeta.kt @@ -3,7 +3,6 @@ package space.kscience.controls.misc import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.long -import space.kscience.dataforge.values.long import java.time.Instant // TODO move to core diff --git a/controls-opcua/build.gradle.kts b/controls-opcua/build.gradle.kts index c5894e4..8fe7564 100644 --- a/controls-opcua/build.gradle.kts +++ b/controls-opcua/build.gradle.kts @@ -4,7 +4,7 @@ plugins { val ktorVersion: String by rootProject.extra -val miloVersion: String = "0.6.3" +val miloVersion: String = "0.6.7" dependencies { api(project(":controls-core")) diff --git a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MetaBsdParser.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MetaBsdParser.kt index 2cfe632..74257af 100644 --- a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MetaBsdParser.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MetaBsdParser.kt @@ -16,7 +16,6 @@ import space.kscience.controls.misc.toMeta import space.kscience.dataforge.meta.* import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName -import space.kscience.dataforge.values.* import java.util.* diff --git a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt index 8686764..84537ef 100644 --- a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt @@ -25,10 +25,10 @@ import space.kscience.controls.api.onPropertyChange import space.kscience.controls.manager.DeviceManager import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MetaSerializer +import space.kscience.dataforge.meta.ValueType import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus -import space.kscience.dataforge.values.ValueType public operator fun Device.get(propertyDescriptor: PropertyDescriptor): Meta? = getProperty(propertyDescriptor.name) diff --git a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/metaToOpc.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/metaToOpc.kt index 5294625..20e85e9 100644 --- a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/metaToOpc.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/metaToOpc.kt @@ -5,10 +5,7 @@ import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime import org.eclipse.milo.opcua.stack.core.types.builtin.StatusCode import org.eclipse.milo.opcua.stack.core.types.builtin.Variant -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.MetaSerializer -import space.kscience.dataforge.meta.isLeaf -import space.kscience.dataforge.values.* +import space.kscience.dataforge.meta.* import java.time.Instant /** diff --git a/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt b/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt index 62ae391..7e4086f 100644 --- a/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt +++ b/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt @@ -3,8 +3,8 @@ package space.kscience.controls.storage import space.kscience.dataforge.context.ContextBuilder import space.kscience.dataforge.io.IOPlugin import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.set import space.kscience.dataforge.meta.string -import space.kscience.dataforge.values.set import java.nio.file.Path import kotlin.io.path.Path diff --git a/demo/all-things/build.gradle.kts b/demo/all-things/build.gradle.kts index 53a57e4..b52b1a5 100644 --- a/demo/all-things/build.gradle.kts +++ b/demo/all-things/build.gradle.kts @@ -24,7 +24,7 @@ dependencies { implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("no.tornado:tornadofx:1.7.20") - implementation("space.kscience:plotlykt-server:0.5.2-dev-2") + implementation("space.kscience:plotlykt-server:0.5.3-dev-1") // implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6") implementation("ch.qos.logback:logback-classic:1.2.11") } diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt index 4abad84..76217ae 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt @@ -6,9 +6,9 @@ import space.kscience.controls.spec.* import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.ValueType import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.transformations.MetaConverter -import space.kscience.dataforge.values.ValueType import java.time.Instant import kotlin.time.Duration.Companion.milliseconds import kotlin.time.ExperimentalTime diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt index 1903416..c304afe 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt @@ -55,7 +55,7 @@ suspend fun Trace.updateXYFrom(flow: Flow>>) { } -suspend fun MagixEndpoint.startDemoDeviceServer(): ApplicationEngine = embeddedServer(CIO, 9090) { +suspend fun MagixEndpoint.startDemoDeviceServer(): ApplicationEngine = embeddedServer(CIO, 9091) { install(WebSockets) install(RSocketSupport) diff --git a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt index 9928e45..49404f0 100644 --- a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt +++ b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt @@ -17,11 +17,11 @@ import space.kscience.controls.ports.* import space.kscience.controls.spec.* import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.asValue import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.transformations.MetaConverter import space.kscience.dataforge.names.NameToken -import space.kscience.dataforge.values.asValue import kotlin.collections.component1 import kotlin.collections.component2 import kotlin.time.Duration diff --git a/magix/magix-java-client/build.gradle.kts b/magix/magix-java-client/build.gradle.kts index 6a76f23..798f9be 100644 --- a/magix/magix-java-client/build.gradle.kts +++ b/magix/magix-java-client/build.gradle.kts @@ -23,4 +23,4 @@ tasks.withType{ kotlinOptions { freeCompilerArgs -= "-Xjdk-release=11" } -} +} \ No newline at end of file From a0fd8913ebab7cbf31742088aae126d01587f147 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 18 Feb 2023 20:05:26 +0300 Subject: [PATCH 66/74] Update versions --- build.gradle.kts | 22 +++++++++-- controls-core/build.gradle.kts | 19 ++++------ controls-magix-client/build.gradle.kts | 17 +++------ controls-storage/build.gradle.kts | 25 +++++------- .../controls-xodus/build.gradle.kts | 2 +- demo/all-things/build.gradle.kts | 4 +- demo/motors/build.gradle.kts | 11 +++--- gradle.properties | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- magix/magix-api/build.gradle.kts | 4 +- magix/magix-java-client/build.gradle.kts | 8 ++-- magix/magix-rsocket/build.gradle.kts | 38 +++++++++---------- .../magix-storage-xodus/build.gradle.kts | 2 +- .../magix/storage/xodus/XodusMagixStorage.kt | 2 +- settings.gradle.kts | 1 + 15 files changed, 81 insertions(+), 78 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index e35fa92..cb7b2e4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,10 +1,15 @@ +import space.kscience.gradle.isInDevelopment +import space.kscience.gradle.useApache2Licence +import space.kscience.gradle.useSPCTeam + plugins { id("space.kscience.gradle.project") } -val dataforgeVersion: String by extra("0.6.0-dev-13") +val dataforgeVersion: String by extra("0.6.0-dev-15") val ktorVersion: String by extra(space.kscience.gradle.KScienceVersions.ktorVersion) val rsocketVersion by extra("0.15.4") +val xodusVersion by extra("2.0.1") allprojects { group = "space.kscience" @@ -12,8 +17,19 @@ allprojects { } ksciencePublish { - github("controls.kt") - space("https://maven.pkg.jetbrains.space/mipt-npm/p/controls/maven") + pom("https://github.com/SciProgCentre/controls.kt") { + useApache2Licence() + useSPCTeam() + } + github("controls.kt", "SciProgCentre") + space( + if (isInDevelopment) { + "https://maven.pkg.jetbrains.space/spc/p/sci/dev" + } else { + "https://maven.pkg.jetbrains.space/spc/p/sci/release" + } + ) + space("https://maven.pkg.jetbrains.space/spc/p/controls/maven") } apiValidation { diff --git a/controls-core/build.gradle.kts b/controls-core/build.gradle.kts index 159e693..8585f17 100644 --- a/controls-core/build.gradle.kts +++ b/controls-core/build.gradle.kts @@ -1,25 +1,20 @@ plugins { id("space.kscience.gradle.mpp") - id("space.kscience.gradle.native") `maven-publish` } val dataforgeVersion: String by rootProject.extra kscience { + jvm() + js() + native() useCoroutines() useSerialization{ json() } -} - -kotlin { - sourceSets { - commonMain{ - dependencies { - api("space.kscience:dataforge-io:$dataforgeVersion") - api(npmlibs.kotlinx.datetime) - } - } + dependencies { + api("space.kscience:dataforge-io:$dataforgeVersion") + api(npmlibs.kotlinx.datetime) } -} \ No newline at end of file +} diff --git a/controls-magix-client/build.gradle.kts b/controls-magix-client/build.gradle.kts index 2105e37..a94e770 100644 --- a/controls-magix-client/build.gradle.kts +++ b/controls-magix-client/build.gradle.kts @@ -4,18 +4,13 @@ plugins { } kscience{ + jvm() + js() useSerialization { json() } -} - -kotlin { - sourceSets { - commonMain { - dependencies { - implementation(project(":magix:magix-rsocket")) - implementation(project(":controls-core")) - } - } + dependencies { + implementation(project(":magix:magix-rsocket")) + implementation(project(":controls-core")) } -} +} \ No newline at end of file diff --git a/controls-storage/build.gradle.kts b/controls-storage/build.gradle.kts index 80210e8..6bf1aa2 100644 --- a/controls-storage/build.gradle.kts +++ b/controls-storage/build.gradle.kts @@ -5,21 +5,16 @@ plugins { val dataforgeVersion: String by rootProject.extra -kotlin { - sourceSets { - commonMain { - dependencies { - api(projects.controlsCore) - } - } - - jvmMain { - dependencies { - api(projects.magix.magixApi) - api(projects.controlsMagixClient) - api(projects.magix.magixServer) - } - } +kscience{ + jvm() + js() + dependencies { + api(projects.controlsCore) + } + dependencies(jvmMain){ + api(projects.magix.magixApi) + api(projects.controlsMagixClient) + api(projects.magix.magixServer) } } diff --git a/controls-storage/controls-xodus/build.gradle.kts b/controls-storage/controls-xodus/build.gradle.kts index a912825..b745b0b 100644 --- a/controls-storage/controls-xodus/build.gradle.kts +++ b/controls-storage/controls-xodus/build.gradle.kts @@ -3,7 +3,7 @@ plugins { `maven-publish` } -val xodusVersion = "2.0.1" +val xodusVersion: String by rootProject.extra dependencies { api(projects.controlsStorage) diff --git a/demo/all-things/build.gradle.kts b/demo/all-things/build.gradle.kts index b52b1a5..1f86f1c 100644 --- a/demo/all-things/build.gradle.kts +++ b/demo/all-things/build.gradle.kts @@ -1,6 +1,6 @@ plugins { kotlin("jvm") - id("org.openjfx.javafxplugin") version "0.0.10" + id("org.openjfx.javafxplugin") version "0.0.13" application } @@ -37,7 +37,7 @@ tasks.withType().configureEach } javafx { - version = "14" + version = "17" modules("javafx.controls") } diff --git a/demo/motors/build.gradle.kts b/demo/motors/build.gradle.kts index 1427557..04f0d72 100644 --- a/demo/motors/build.gradle.kts +++ b/demo/motors/build.gradle.kts @@ -1,11 +1,16 @@ plugins { id("space.kscience.gradle.jvm") - `maven-publish` application + id("org.openjfx.javafxplugin") } //TODO to be moved to a separate project +javafx { + version = "17" + modules = listOf("javafx.controls") +} + application{ mainClass.set("ru.mipt.npm.devices.pimotionmaster.PiMotionMasterAppKt") } @@ -14,10 +19,6 @@ kotlin{ explicitApi = null } -kscience{ - useFx(space.kscience.gradle.FXModule.CONTROLS, configuration = space.kscience.gradle.DependencyConfiguration.IMPLEMENTATION) -} - val ktorVersion: String by rootProject.extra val dataforgeVersion: String by extra diff --git a/gradle.properties b/gradle.properties index 52d10eb..05b0759 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,4 +7,4 @@ org.gradle.parallel=true publishing.github=false publishing.sonatype=false -toolsVersion=0.12.0-kotlin-1.7.20-Beta \ No newline at end of file +toolsVersion=0.14.2-kotlin-1.8.10 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index aa991fc..070cb70 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/magix/magix-api/build.gradle.kts b/magix/magix-api/build.gradle.kts index 0a23e4c..5f8bdf3 100644 --- a/magix/magix-api/build.gradle.kts +++ b/magix/magix-api/build.gradle.kts @@ -1,10 +1,12 @@ plugins { id("space.kscience.gradle.mpp") - id("space.kscience.gradle.native") `maven-publish` } kscience { + jvm() + js() + native() useCoroutines() useSerialization{ json() diff --git a/magix/magix-java-client/build.gradle.kts b/magix/magix-java-client/build.gradle.kts index 798f9be..63041b3 100644 --- a/magix/magix-java-client/build.gradle.kts +++ b/magix/magix-java-client/build.gradle.kts @@ -12,10 +12,10 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:${KScienceVersions.coroutinesVersion}") } -java { - sourceCompatibility = KScienceVersions.JVM_TARGET - targetCompatibility = KScienceVersions.JVM_TARGET -} +//java { +// sourceCompatibility = KScienceVersions.JVM_TARGET +// targetCompatibility = KScienceVersions.JVM_TARGET +//} //FIXME https://youtrack.jetbrains.com/issue/KT-52815/Compiler-option-Xjdk-release-fails-to-compile-mixed-projects diff --git a/magix/magix-rsocket/build.gradle.kts b/magix/magix-rsocket/build.gradle.kts index 7f637b3..39aa8aa 100644 --- a/magix/magix-rsocket/build.gradle.kts +++ b/magix/magix-rsocket/build.gradle.kts @@ -1,6 +1,5 @@ plugins { id("space.kscience.gradle.mpp") - id("space.kscience.gradle.native") `maven-publish` } @@ -8,30 +7,29 @@ description = """ Magix endpoint (client) based on RSocket """.trimIndent() -kscience { - useSerialization { - json() - } -} - val ktorVersion: String by rootProject.extra val rsocketVersion: String by rootProject.extra +kscience { + jvm() + js() + native() + useSerialization { + json() + } + dependencies { + api(projects.magix.magixApi) + implementation("io.ktor:ktor-client-core:$ktorVersion") + implementation("io.rsocket.kotlin:rsocket-ktor-client:$rsocketVersion") + } + dependencies(jvmMain) { + implementation("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") + } +} + kotlin { sourceSets { - commonMain { - dependencies { - api(projects.magix.magixApi) - implementation("io.ktor:ktor-client-core:$ktorVersion") - implementation("io.rsocket.kotlin:rsocket-ktor-client:$rsocketVersion") - } - } - jvmMain { - dependencies { - implementation("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") - } - } - linuxX64Main{ + getByName("linuxX64Main") { dependencies { implementation("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") } diff --git a/magix/magix-storage/magix-storage-xodus/build.gradle.kts b/magix/magix-storage/magix-storage-xodus/build.gradle.kts index 9019cf1..63a1b44 100644 --- a/magix/magix-storage/magix-storage-xodus/build.gradle.kts +++ b/magix/magix-storage/magix-storage-xodus/build.gradle.kts @@ -3,7 +3,7 @@ plugins { `maven-publish` } -val xodusVersion = "2.0.1" +val xodusVersion: String by rootProject.extra kscience{ useCoroutines() diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt index 87e4096..39eb4ea 100644 --- a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt @@ -46,7 +46,7 @@ public class XodusMagixStorage( } }.launchIn(scope) - private fun Entity.parseMagixMessage(): MagixMessage = MagixMessage( + private fun Entity.parseMagixMessage(): MagixMessage = MagixMessage( format = getProperty(MagixMessage::format.name).toString(), payload = getBlobString(MagixMessage::payload.name)?.let { magixJson.parseToJsonElement(it) diff --git a/settings.gradle.kts b/settings.gradle.kts index 5b2bf00..de36d30 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,6 +19,7 @@ pluginManagement { id("space.kscience.gradle.mpp") version toolsVersion id("space.kscience.gradle.jvm") version toolsVersion id("space.kscience.gradle.js") version toolsVersion + id("org.openjfx.javafxplugin") version "0.0.13" } } From 68805d6f42bca9917d64eb21bb37128a9b9373f8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 25 Feb 2023 10:53:53 +0300 Subject: [PATCH 67/74] Refactor onOpen in device spec --- build.gradle.kts | 6 +++- .../kscience/controls/api/DeviceMessage.kt | 4 +-- .../kscience/controls/spec/DeviceBase.kt | 14 ++++++++-- .../kscience/controls/spec/DeviceSpec.kt | 8 ++++++ .../controls/spec/deviceExtensions.kt | 2 +- .../controls/demo/DemoControllerView.kt | 2 +- .../kscience/controls/demo/DemoDevice.kt | 28 +++++++++---------- .../controls/demo/demoDeviceServer.kt | 1 + .../space/kscience/magix/server/server.kt | 1 + 9 files changed, 45 insertions(+), 21 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index cb7b2e4..ecac2af 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("space.kscience.gradle.project") } -val dataforgeVersion: String by extra("0.6.0-dev-15") +val dataforgeVersion: String by extra("0.6.1-dev-4") val ktorVersion: String by extra(space.kscience.gradle.KScienceVersions.ktorVersion) val rsocketVersion by extra("0.15.4") val xodusVersion by extra("2.0.1") @@ -14,6 +14,10 @@ val xodusVersion by extra("2.0.1") allprojects { group = "space.kscience" version = "0.1.1-SNAPSHOT" + repositories{ + mavenCentral() + mavenLocal() + } } ksciencePublish { diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt index 8f6f0f9..80a7f9d 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt @@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.encodeToJsonElement -import space.kscience.dataforge.io.SimpleEnvelope +import space.kscience.dataforge.io.Envelope import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.toJson import space.kscience.dataforge.meta.toMeta @@ -221,4 +221,4 @@ public data class DeviceErrorMessage( public fun DeviceMessage.toMeta(): Meta = Json.encodeToJsonElement(this).toMeta() -public fun DeviceMessage.toEnvelope(): SimpleEnvelope = SimpleEnvelope(toMeta(), null) +public fun DeviceMessage.toEnvelope(): Envelope = Envelope(toMeta(), null) diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt index 397eff7..4c114b7 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt @@ -20,8 +20,8 @@ public abstract class DeviceBase>( override val meta: Meta = Meta.EMPTY ) : Device { - public abstract val properties: Map> //get() = spec.properties - public abstract val actions: Map> //get() = spec.actions + public abstract val properties: Map> + public abstract val actions: Map> override val propertyDescriptors: Collection get() = properties.values.map { it.descriptor } @@ -136,5 +136,15 @@ public open class DeviceBySpec>( ) : DeviceBase(context, meta) { override val properties: Map> get() = spec.properties override val actions: Map> get() = spec.actions + + override suspend fun open(): Unit = with(spec){ + super.open() + self.onOpen() + } + + override fun close(): Unit = with(spec){ + self.onClose() + super.close() + } } diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt index 67552ea..1cf567c 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt @@ -23,6 +23,14 @@ public abstract class DeviceSpec { private val _actions = HashMap>() public val actions: Map> get() = _actions + + public open suspend fun D.onOpen(){ + } + + public open fun D.onClose(){ + } + + public fun > registerProperty(deviceProperty: P): P { _properties[deviceProperty.name] = deviceProperty return deviceProperty diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/deviceExtensions.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/deviceExtensions.kt index 3a9c280..ed48d39 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/deviceExtensions.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/deviceExtensions.kt @@ -22,7 +22,7 @@ public fun , R> D.readRecurring(interval: Duration, reader: su } /** - * Do a recurring task on a device. The task could + * Do a recurring (with a fixed delay) task on a device. */ public fun > D.doRecurring(interval: Duration, task: suspend D.() -> Unit): Job = launch { while (isActive) { diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt index 4decac5..cefe834 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt @@ -43,7 +43,7 @@ class DemoController : Controller(), ContextAware { plugin(DeviceManager) } - private val deviceManager = context.fetch(DeviceManager) + private val deviceManager = context.request(DeviceManager) fun init() { context.launch { diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt index 76217ae..10713f3 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt @@ -11,7 +11,6 @@ import space.kscience.dataforge.meta.descriptors.value import space.kscience.dataforge.meta.transformations.MetaConverter import java.time.Instant import kotlin.time.Duration.Companion.milliseconds -import kotlin.time.ExperimentalTime class DemoDevice(context: Context, meta: Meta) : DeviceBySpec(DemoDevice, context, meta) { @@ -19,19 +18,6 @@ class DemoDevice(context: Context, meta: Meta) : DeviceBySpec(DemoDe private var sinScaleState = 1.0 private var cosScaleState = 1.0 - @OptIn(ExperimentalTime::class) - override suspend fun open() { - super.open() - launch { - sinScale.read() - cosScale.read() - timeScale.read() - } - doRecurring(50.milliseconds) { - coordinates.read() - } - } - companion object : DeviceSpec(), Factory { @@ -73,6 +59,20 @@ class DemoDevice(context: Context, meta: Meta) : DeviceBySpec(DemoDe } } + + override suspend fun DemoDevice.onOpen() { + launch { + sinScale.read() + cosScale.read() + timeScale.read() + } + doRecurring(50.milliseconds) { + sin.read() + cos.read() + coordinates.read() + } + } + val resetScale by action(MetaConverter.meta, MetaConverter.meta) { timeScale.write(5000.0) sinScale.write(1.0) diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt index c304afe..5b6c7cc 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt @@ -55,6 +55,7 @@ suspend fun Trace.updateXYFrom(flow: Flow>>) { } +@Suppress("ExtractKtorModule") suspend fun MagixEndpoint.startDemoDeviceServer(): ApplicationEngine = embeddedServer(CIO, 9091) { install(WebSockets) install(RSocketSupport) diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/server.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/server.kt index 8ed3ba5..b37d5ef 100644 --- a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/server.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/server.kt @@ -70,6 +70,7 @@ public fun CoroutineScope.startMagixServer( ) } + @Suppress("ExtractKtorModule") return embeddedServer(CIO, host = "localhost", port = port) { magixModule(magixFlow) applicationConfiguration(magixFlow) From fe24c0ee313445664ec12f16dbf3eabcf0300f64 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 25 Feb 2023 10:55:21 +0300 Subject: [PATCH 68/74] Add magix-rfc as a submodule --- .gitmodules | 3 +++ magix/rfc | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 magix/rfc diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..bbe1eed --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "magix/rfc"] + path = magix/rfc + url = https://github.com/waltz-controls/rfc diff --git a/magix/rfc b/magix/rfc new file mode 160000 index 0000000..5ae42aa --- /dev/null +++ b/magix/rfc @@ -0,0 +1 @@ +Subproject commit 5ae42aa297fbd2ab2239601f064e1d1239487590 From 2a386568f98c067d67931eb6b350e7258cbcc317 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 25 Feb 2023 17:45:09 +0300 Subject: [PATCH 69/74] Refactor magix server flow plugins --- .../controls/server/deviceWebServer.kt | 35 +++++---- .../controls/demo/DemoControllerView.kt | 4 +- .../controls/demo/car/VirtualCarController.kt | 4 +- .../space/kscience/controls/demo/echo/main.kt | 10 ++- demo/magix-demo/src/main/kotlin/zmq.kt | 11 ++- .../kscience/magix/api/MagixFlowPlugin.kt | 9 +++ .../kscience/magix/server/RSocketMagix.kt | 74 +++++++++++++++++++ .../space/kscience/magix/server/ZmqMagix.kt | 53 +++++++++++++ .../kscience/magix/server/magixModule.kt | 52 +------------ .../space/kscience/magix/server/server.kt | 56 ++------------ .../magix/server/zmqMagixServerSocket.kt | 48 ------------ 11 files changed, 180 insertions(+), 176 deletions(-) create mode 100644 magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFlowPlugin.kt create mode 100644 magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagix.kt create mode 100644 magix/magix-server/src/main/kotlin/space/kscience/magix/server/ZmqMagix.kt delete mode 100644 magix/magix-server/src/main/kotlin/space/kscience/magix/server/zmqMagixServerSocket.kt diff --git a/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt b/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt index 434aaa0..596758c 100644 --- a/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt +++ b/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt @@ -36,8 +36,8 @@ import space.kscience.dataforge.meta.toMeta import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.asName import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixFlowPlugin import space.kscience.magix.api.MagixMessage -import space.kscience.magix.server.launchMagixServerRawRSocket import space.kscience.magix.server.magixModule /** @@ -47,26 +47,23 @@ public fun CoroutineScope.startDeviceServer( manager: DeviceManager, port: Int = MagixEndpoint.DEFAULT_MAGIX_HTTP_PORT, host: String = "localhost", -): ApplicationEngine { - - return this.embeddedServer(CIO, port, host) { - install(WebSockets) +): ApplicationEngine = embeddedServer(CIO, port, host) { + install(WebSockets) // install(CORS) { // anyHost() // } - install(StatusPages) { - exception { call, cause -> - call.respond(HttpStatusCode.BadRequest, cause.message ?: "") - } + install(StatusPages) { + exception { call, cause -> + call.respond(HttpStatusCode.BadRequest, cause.message ?: "") } - deviceManagerModule(manager) - routing { - get("/") { - call.respondRedirect("/dashboard") - } + } + deviceManagerModule(manager) + routing { + get("/") { + call.respondRedirect("/dashboard") } - }.start() -} + } +}.start() public fun ApplicationEngine.whenStarted(callback: Application.() -> Unit) { environment.monitor.subscribe(ApplicationStarted, callback) @@ -77,9 +74,9 @@ public val WEB_SERVER_TARGET: Name = "@webServer".asName() public fun Application.deviceManagerModule( manager: DeviceManager, + vararg plugins: MagixFlowPlugin, deviceNames: Collection = manager.devices.keys.map { it.toString() }, route: String = "/", - rawSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_RAW_PORT, buffer: Int = 100, ) { if (pluginOrNull(WebSockets) == null) { @@ -217,6 +214,8 @@ public fun Application.deviceManagerModule( extraBufferCapacity = buffer ) - launchMagixServerRawRSocket(magixFlow, rawSocketPort) + plugins.forEach { + it.start(this, magixFlow) + } magixModule(magixFlow) } \ No newline at end of file diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt index cefe834..d2cd259 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt @@ -21,6 +21,8 @@ import space.kscience.dataforge.context.* import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.rsocket.rSocketWithTcp import space.kscience.magix.rsocket.rSocketWithWebSockets +import space.kscience.magix.server.RSocketMagix +import space.kscience.magix.server.ZmqMagix import space.kscience.magix.server.startMagixServer import tornadofx.* import java.awt.Desktop @@ -49,7 +51,7 @@ class DemoController : Controller(), ContextAware { context.launch { device = deviceManager.install("demo", DemoDevice) //starting magix event loop - magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) + magixServer = startMagixServer(RSocketMagix(), ZmqMagix()) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost") deviceManager.connectToMagix(deviceEndpoint) diff --git a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt index 5e6651e..fc78bee 100644 --- a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt @@ -18,6 +18,8 @@ import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.rsocket.rSocketWithTcp +import space.kscience.magix.server.RSocketMagix +import space.kscience.magix.server.ZmqMagix import space.kscience.magix.server.startMagixServer import space.kscience.magix.storage.xodus.storeInXodus import tornadofx.* @@ -47,7 +49,7 @@ class VirtualCarController : Controller(), ContextAware { virtualCar = deviceManager.install("virtual-car", VirtualCar) //starting magix event loop and connect it to entity store - magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) + magixServer = startMagixServer(RSocketMagix(), ZmqMagix()) storageEndpoint = MagixEndpoint.rSocketWithTcp("localhost").apply { storeInXodus(this@launch, magixEntityStorePath) diff --git a/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt b/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt index a02c044..3dbb43d 100644 --- a/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt +++ b/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt @@ -1,11 +1,12 @@ package space.kscience.controls.demo.echo -import io.ktor.server.application.log import kotlinx.coroutines.* import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.serialization.json.JsonObject +import org.slf4j.LoggerFactory import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixFlowPlugin import space.kscience.magix.api.MagixMessage import space.kscience.magix.api.MagixMessageFilter import space.kscience.magix.rsocket.rSocketStreamWithWebSockets @@ -59,16 +60,17 @@ private suspend fun MagixEndpoint.collectEcho(scope: CoroutineScope, n: Int) { @OptIn(ExperimentalTime::class) suspend fun main(): Unit = coroutineScope { launch(Dispatchers.Default) { - val server = startMagixServer(enableRawRSocket = false, enableZmq = false) { flow -> + val server = startMagixServer(MagixFlowPlugin { _, flow -> + val logger = LoggerFactory.getLogger("echo") //echo each message flow.onEach { message -> if (message.parentId == null) { val m = message.copy(origin = "loop", parentId = message.id, id = message.id + ".response") - log.info("echo: $m") + logger.info(m.toString()) flow.emit(m) } }.launchIn(this) - } + }) val responseTime = measureTime { diff --git a/demo/magix-demo/src/main/kotlin/zmq.kt b/demo/magix-demo/src/main/kotlin/zmq.kt index 6a7e01d..fbb2ad6 100644 --- a/demo/magix-demo/src/main/kotlin/zmq.kt +++ b/demo/magix-demo/src/main/kotlin/zmq.kt @@ -9,6 +9,8 @@ import kotlinx.serialization.json.* import org.slf4j.LoggerFactory import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixMessage +import space.kscience.magix.server.RSocketMagix +import space.kscience.magix.server.ZmqMagix import space.kscience.magix.server.startMagixServer import space.kscince.magix.zmq.ZmqMagixEndpoint import java.awt.Desktop @@ -22,7 +24,7 @@ suspend fun MagixEndpoint.sendJson( id: String? = null, parentId: String? = null, user: JsonElement? = null, - builder: JsonObjectBuilder.() -> Unit + builder: JsonObjectBuilder.() -> Unit, ): Unit = broadcast(MagixMessage(format, buildJsonObject(builder), origin, target, id, parentId, user)) internal const val numberOfMessages = 100 @@ -30,10 +32,7 @@ internal const val numberOfMessages = 100 suspend fun main(): Unit = coroutineScope { val logger = LoggerFactory.getLogger("magix-demo") logger.info("Starting magix server") - val server = startMagixServer( - buffer = 10, - enableRawRSocket = false //Disable rsocket to avoid kotlin 1.5 compatibility issue - ) + val server = startMagixServer(RSocketMagix(), ZmqMagix(), buffer = 10) server.apply { val host = "localhost"//environment.connectors.first().host @@ -44,7 +43,7 @@ suspend fun main(): Unit = coroutineScope { logger.info("Starting client") //Create zmq magix endpoint and wait for to finish - ZmqMagixEndpoint("localhost","tcp").use { client -> + ZmqMagixEndpoint("localhost", "tcp").use { client -> logger.info("Starting subscription") client.subscribe().onEach { println(it.payload) diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFlowPlugin.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFlowPlugin.kt new file mode 100644 index 0000000..8cf9ccd --- /dev/null +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFlowPlugin.kt @@ -0,0 +1,9 @@ +package space.kscience.magix.api + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableSharedFlow + +public fun interface MagixFlowPlugin { + public fun start(scope: CoroutineScope, magixFlow: MutableSharedFlow): Job +} \ No newline at end of file diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagix.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagix.kt new file mode 100644 index 0000000..8deafdf --- /dev/null +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagix.kt @@ -0,0 +1,74 @@ +package space.kscience.magix.server + +import io.rsocket.kotlin.ConnectionAcceptor +import io.rsocket.kotlin.RSocketRequestHandler +import io.rsocket.kotlin.core.RSocketServer +import io.rsocket.kotlin.payload.Payload +import io.rsocket.kotlin.payload.buildPayload +import io.rsocket.kotlin.payload.data +import io.rsocket.kotlin.transport.ktor.tcp.TcpServer +import io.rsocket.kotlin.transport.ktor.tcp.TcpServerTransport +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.* +import kotlinx.serialization.encodeToString +import space.kscience.magix.api.* +import space.kscience.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_RAW_PORT + +/** + * Raw TCP magix server + */ +public class RSocketMagix(public val port: Int = DEFAULT_MAGIX_RAW_PORT): MagixFlowPlugin { + override fun start(scope: CoroutineScope, magixFlow: MutableSharedFlow): Job { + val tcpTransport = TcpServerTransport(port = port) + val rSocketJob: TcpServer = RSocketServer().bindIn(scope, tcpTransport, acceptor(scope, magixFlow)) + + scope.coroutineContext[Job]?.invokeOnCompletion { + rSocketJob.handlerJob.cancel() + } + + return rSocketJob.handlerJob + } + + public companion object{ + public fun acceptor( + coroutineScope: CoroutineScope, + magixFlow: MutableSharedFlow, + ): ConnectionAcceptor = ConnectionAcceptor { + RSocketRequestHandler(coroutineScope.coroutineContext) { + //handler for request/stream + requestStream { request: Payload -> + val filter = MagixEndpoint.magixJson.decodeFromString(MagixMessageFilter.serializer(), request.data.readText()) + magixFlow.filter(filter).map { message -> + val string = MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message) + buildPayload { data(string) } + } + } + //single send + fireAndForget { request: Payload -> + val message = MagixEndpoint.magixJson.decodeFromString(MagixMessage.serializer(), request.data.readText()) + magixFlow.emit(message) + } + // bi-directional connection + requestChannel { request: Payload, input: Flow -> + input.onEach { + magixFlow.emit(MagixEndpoint.magixJson.decodeFromString(MagixMessage.serializer(), it.data.readText())) + }.launchIn(this) + + val filterText = request.data.readText() + + val filter = if(filterText.isNotBlank()){ + MagixEndpoint.magixJson.decodeFromString(MagixMessageFilter.serializer(), filterText) + } else { + MagixMessageFilter() + } + + magixFlow.filter(filter).map { message -> + val string = MagixEndpoint.magixJson.encodeToString(message) + buildPayload { data(string) } + } + } + } + } + } +} \ No newline at end of file diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/ZmqMagix.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/ZmqMagix.kt new file mode 100644 index 0000000..c2e54a0 --- /dev/null +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/ZmqMagix.kt @@ -0,0 +1,53 @@ +package space.kscience.magix.server + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import org.slf4j.LoggerFactory +import org.zeromq.SocketType +import org.zeromq.ZContext +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixFlowPlugin +import space.kscience.magix.api.MagixMessage + + +public class ZmqMagix( + public val localHost: String = "tcp://*", + public val zmqPubSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT, + public val zmqPullSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, +) : MagixFlowPlugin { + override fun start(scope: CoroutineScope, magixFlow: MutableSharedFlow): Job = + scope.launch(Dispatchers.IO) { + val logger = LoggerFactory.getLogger("magix-server-zmq") + + ZContext().use { context -> + //launch publishing job + val pubSocket = context.createSocket(SocketType.PUB) + pubSocket.bind("$localHost:$zmqPubSocketPort") + magixFlow.onEach { message -> + val string = MagixEndpoint.magixJson.encodeToString(message) + pubSocket.send(string) + logger.debug("Published: $string") + }.launchIn(this) + + //launch pulling job + val pullSocket = context.createSocket(SocketType.PULL) + pullSocket.bind("$localHost:$zmqPullSocketPort") + pullSocket.receiveTimeOut = 500 + //suspending loop while pulling is active + while (isActive) { + val string: String? = pullSocket.recvStr() + if (string != null) { + logger.debug("Received: $string") + val message = MagixEndpoint.magixJson.decodeFromString(string) + magixFlow.emit(message) + } + } + } + } + + +} \ No newline at end of file diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt index 50c5921..e9bedd3 100644 --- a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt @@ -8,15 +8,10 @@ import io.ktor.server.request.receive import io.ktor.server.routing.* import io.ktor.server.util.getValue import io.ktor.server.websocket.WebSockets -import io.rsocket.kotlin.ConnectionAcceptor -import io.rsocket.kotlin.RSocketRequestHandler import io.rsocket.kotlin.ktor.server.RSocketSupport import io.rsocket.kotlin.ktor.server.rSocket -import io.rsocket.kotlin.payload.Payload -import io.rsocket.kotlin.payload.buildPayload -import io.rsocket.kotlin.payload.data -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.map import kotlinx.html.* import kotlinx.serialization.encodeToString import space.kscience.magix.api.MagixEndpoint.Companion.magixJson @@ -26,45 +21,6 @@ import space.kscience.magix.api.filter import java.util.* -internal fun CoroutineScope.magixAcceptor( - magixFlow: MutableSharedFlow, -): ConnectionAcceptor = ConnectionAcceptor { - RSocketRequestHandler(coroutineContext) { - //handler for request/stream - requestStream { request: Payload -> - val filter = magixJson.decodeFromString(MagixMessageFilter.serializer(), request.data.readText()) - magixFlow.filter(filter).map { message -> - val string = magixJson.encodeToString(MagixMessage.serializer(), message) - buildPayload { data(string) } - } - } - //single send - fireAndForget { request: Payload -> - val message = magixJson.decodeFromString(MagixMessage.serializer(), request.data.readText()) - magixFlow.emit(message) - } - // bi-directional connection - requestChannel { request: Payload, input: Flow -> - input.onEach { - magixFlow.emit(magixJson.decodeFromString(MagixMessage.serializer(), it.data.readText())) - }.launchIn(this) - - val filterText = request.data.readText() - - val filter = if(filterText.isNotBlank()){ - magixJson.decodeFromString(MagixMessageFilter.serializer(), filterText) - } else { - MagixMessageFilter() - } - - magixFlow.filter(filter).map { message -> - val string = magixJson.encodeToString(message) - buildPayload { data(string) } - } - } - } -} - /** * Create a message filter from call parameters */ @@ -84,7 +40,7 @@ private fun ApplicationCall.buildFilter(): MagixMessageFilter { } /** - * Attache magix http/sse and websocket-based rsocket event loop + statistics page to existing [MutableSharedFlow] + * Attach magix http/sse and websocket-based rsocket event loop + statistics page to existing [MutableSharedFlow] */ public fun Application.magixModule(magixFlow: MutableSharedFlow, route: String = "/") { if (pluginOrNull(WebSockets) == null) { @@ -149,7 +105,7 @@ public fun Application.magixModule(magixFlow: MutableSharedFlow, r magixFlow.emit(message) } //rSocket server. Filter from Payload - rSocket("rsocket", acceptor = application.magixAcceptor(magixFlow)) + rSocket("rsocket", acceptor = RSocketMagix.acceptor( application, magixFlow)) } } } diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/server.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/server.kt index b37d5ef..2396e25 100644 --- a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/server.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/server.kt @@ -1,80 +1,36 @@ package space.kscience.magix.server -import io.ktor.server.application.Application import io.ktor.server.cio.CIO import io.ktor.server.engine.ApplicationEngine import io.ktor.server.engine.embeddedServer -import io.rsocket.kotlin.core.RSocketServer -import io.rsocket.kotlin.transport.ktor.tcp.TcpServer -import io.rsocket.kotlin.transport.ktor.tcp.TcpServerTransport import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow -import org.slf4j.LoggerFactory -import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_HTTP_PORT -import space.kscience.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_RAW_PORT +import space.kscience.magix.api.MagixFlowPlugin import space.kscience.magix.api.MagixMessage -/** - * Raw TCP magix server - */ -public fun CoroutineScope.launchMagixServerRawRSocket( - magixFlow: MutableSharedFlow, - rawSocketPort: Int = DEFAULT_MAGIX_RAW_PORT, -): TcpServer { - val tcpTransport = TcpServerTransport(port = rawSocketPort) - val rSocketJob: TcpServer = RSocketServer().bindIn(this, tcpTransport, magixAcceptor(magixFlow)) - - coroutineContext[Job]?.invokeOnCompletion { - rSocketJob.handlerJob.cancel() - } - - return rSocketJob; -} /** * A combined RSocket/TCP/ZMQ server - * @param applicationConfiguration optional additional configuration for magix loop server */ public fun CoroutineScope.startMagixServer( + vararg plugins: MagixFlowPlugin, port: Int = DEFAULT_MAGIX_HTTP_PORT, buffer: Int = 1000, - enableRawRSocket: Boolean = true, - rawRSocketPort: Int = DEFAULT_MAGIX_RAW_PORT, - enableZmq: Boolean = true, - zmqPubSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT, - zmqPullSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, - applicationConfiguration: Application.(MutableSharedFlow) -> Unit = {}, ): ApplicationEngine { - val logger = LoggerFactory.getLogger("magix-server") + val magixFlow = MutableSharedFlow( replay = buffer, extraBufferCapacity = buffer, onBufferOverflow = BufferOverflow.DROP_OLDEST ) - if (enableRawRSocket) { - //Start tcpRSocket server - logger.info("Starting magix raw rsocket server on port $rawRSocketPort") - launchMagixServerRawRSocket(magixFlow, rawRSocketPort) - } - if (enableZmq) { - //Start ZMQ server socket pair - logger.info("Starting magix zmq server on pub port $zmqPubSocketPort and pull port $zmqPullSocketPort") - launchMagixServerZmqSocket( - magixFlow, - zmqPubSocketPort = zmqPubSocketPort, - zmqPullSocketPort = zmqPullSocketPort - ) + plugins.forEach { + it.start(this, magixFlow) } - @Suppress("ExtractKtorModule") - return embeddedServer(CIO, host = "localhost", port = port) { - magixModule(magixFlow) - applicationConfiguration(magixFlow) - }.apply { + return embeddedServer(CIO, host = "localhost", port = port, module = { magixModule(magixFlow) }).apply { start() } } \ No newline at end of file diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/zmqMagixServerSocket.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/zmqMagixServerSocket.kt deleted file mode 100644 index 18717e4..0000000 --- a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/zmqMagixServerSocket.kt +++ /dev/null @@ -1,48 +0,0 @@ -package space.kscience.magix.server - -import kotlinx.coroutines.* -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.serialization.decodeFromString -import kotlinx.serialization.encodeToString -import org.slf4j.LoggerFactory -import org.zeromq.SocketType -import org.zeromq.ZContext -import space.kscience.magix.api.MagixEndpoint -import space.kscience.magix.api.MagixMessage - -public fun CoroutineScope.launchMagixServerZmqSocket( - magixFlow: MutableSharedFlow, - localHost: String = "tcp://*", - zmqPubSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT, - zmqPullSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, -): Job = launch(Dispatchers.IO) { - val logger = LoggerFactory.getLogger("magix-server-zmq") - - ZContext().use { context -> - //launch publishing job - val pubSocket = context.createSocket(SocketType.PUB) - pubSocket.bind("$localHost:$zmqPubSocketPort") - magixFlow.onEach { message -> - val string = MagixEndpoint.magixJson.encodeToString(message) - pubSocket.send(string) - logger.debug("Published: $string") - }.launchIn(this) - - //launch pulling job - val pullSocket = context.createSocket(SocketType.PULL) - pullSocket.bind("$localHost:$zmqPullSocketPort") - pullSocket.receiveTimeOut = 500 - //suspending loop while pulling is active - while (isActive) { - val string: String? = pullSocket.recvStr() - if (string != null) { - logger.debug("Received: $string") - val message = MagixEndpoint.magixJson.decodeFromString(string) - magixFlow.emit(message) - } - } - } -} - From fca718cfc4cdf69ecbe4cca0ee1cb46cf10c79a9 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 25 Feb 2023 18:15:19 +0300 Subject: [PATCH 70/74] Move ZMQ to a separate module --- .../controls/server/deviceWebServer.kt | 22 +++++++++++-------- .../controls/demo/DemoControllerView.kt | 6 ++--- demo/car/build.gradle.kts | 1 + .../controls/demo/car/VirtualCarController.kt | 6 ++--- demo/magix-demo/src/main/kotlin/zmq.kt | 6 ++--- magix/magix-server/build.gradle.kts | 2 -- ...cketMagix.kt => RSocketMagixFlowPlugin.kt} | 2 +- .../kscience/magix/server/magixModule.kt | 2 +- magix/magix-zmq/build.gradle.kts | 1 + .../kscince/magix/zmq/ZmqMagixFlowPlugin.kt} | 4 ++-- 10 files changed, 28 insertions(+), 24 deletions(-) rename magix/magix-server/src/main/kotlin/space/kscience/magix/server/{RSocketMagix.kt => RSocketMagixFlowPlugin.kt} (96%) rename magix/{magix-server/src/main/kotlin/space/kscience/magix/server/ZmqMagix.kt => magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt} (96%) diff --git a/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt b/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt index 596758c..65ffb07 100644 --- a/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt +++ b/controls-server/src/main/kotlin/space/kscience/controls/server/deviceWebServer.kt @@ -40,14 +40,9 @@ import space.kscience.magix.api.MagixFlowPlugin import space.kscience.magix.api.MagixMessage import space.kscience.magix.server.magixModule -/** - * Create and start a web server for several devices - */ -public fun CoroutineScope.startDeviceServer( - manager: DeviceManager, - port: Int = MagixEndpoint.DEFAULT_MAGIX_HTTP_PORT, - host: String = "localhost", -): ApplicationEngine = embeddedServer(CIO, port, host) { + + +private fun Application.deviceServerModule(manager: DeviceManager) { install(WebSockets) // install(CORS) { // anyHost() @@ -63,7 +58,16 @@ public fun CoroutineScope.startDeviceServer( call.respondRedirect("/dashboard") } } -}.start() +} + +/** + * Create and start a web server for several devices + */ +public fun CoroutineScope.startDeviceServer( + manager: DeviceManager, + port: Int = MagixEndpoint.DEFAULT_MAGIX_HTTP_PORT, + host: String = "localhost", +): ApplicationEngine = embeddedServer(CIO, port, host, module = { deviceServerModule(manager) }).start() public fun ApplicationEngine.whenStarted(callback: Application.() -> Unit) { environment.monitor.subscribe(ApplicationStarted, callback) diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt index d2cd259..4fe6798 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt @@ -21,9 +21,9 @@ import space.kscience.dataforge.context.* import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.rsocket.rSocketWithTcp import space.kscience.magix.rsocket.rSocketWithWebSockets -import space.kscience.magix.server.RSocketMagix -import space.kscience.magix.server.ZmqMagix +import space.kscience.magix.server.RSocketMagixFlowPlugin import space.kscience.magix.server.startMagixServer +import space.kscince.magix.zmq.ZmqMagixFlowPlugin import tornadofx.* import java.awt.Desktop import java.net.URI @@ -51,7 +51,7 @@ class DemoController : Controller(), ContextAware { context.launch { device = deviceManager.install("demo", DemoDevice) //starting magix event loop - magixServer = startMagixServer(RSocketMagix(), ZmqMagix()) + magixServer = startMagixServer(RSocketMagixFlowPlugin(), ZmqMagixFlowPlugin()) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost") deviceManager.connectToMagix(deviceEndpoint) diff --git a/demo/car/build.gradle.kts b/demo/car/build.gradle.kts index 09555d6..27e5776 100644 --- a/demo/car/build.gradle.kts +++ b/demo/car/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { implementation(projects.magix.magixApi) implementation(projects.magix.magixServer) implementation(projects.magix.magixRsocket) + implementation(projects.magix.magixZmq) implementation(projects.controlsMagixClient) implementation(projects.controlsStorage.controlsXodus) implementation(projects.magix.magixStorage.magixStorageXodus) diff --git a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt index fc78bee..1f98ef7 100644 --- a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt @@ -18,10 +18,10 @@ import space.kscience.dataforge.context.* import space.kscience.dataforge.meta.Meta import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.rsocket.rSocketWithTcp -import space.kscience.magix.server.RSocketMagix -import space.kscience.magix.server.ZmqMagix +import space.kscience.magix.server.RSocketMagixFlowPlugin import space.kscience.magix.server.startMagixServer import space.kscience.magix.storage.xodus.storeInXodus +import space.kscince.magix.zmq.ZmqMagixFlowPlugin import tornadofx.* import java.nio.file.Paths @@ -49,7 +49,7 @@ class VirtualCarController : Controller(), ContextAware { virtualCar = deviceManager.install("virtual-car", VirtualCar) //starting magix event loop and connect it to entity store - magixServer = startMagixServer(RSocketMagix(), ZmqMagix()) + magixServer = startMagixServer(RSocketMagixFlowPlugin(), ZmqMagixFlowPlugin()) storageEndpoint = MagixEndpoint.rSocketWithTcp("localhost").apply { storeInXodus(this@launch, magixEntityStorePath) diff --git a/demo/magix-demo/src/main/kotlin/zmq.kt b/demo/magix-demo/src/main/kotlin/zmq.kt index fbb2ad6..7ac9974 100644 --- a/demo/magix-demo/src/main/kotlin/zmq.kt +++ b/demo/magix-demo/src/main/kotlin/zmq.kt @@ -9,10 +9,10 @@ import kotlinx.serialization.json.* import org.slf4j.LoggerFactory import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixMessage -import space.kscience.magix.server.RSocketMagix -import space.kscience.magix.server.ZmqMagix +import space.kscience.magix.server.RSocketMagixFlowPlugin import space.kscience.magix.server.startMagixServer import space.kscince.magix.zmq.ZmqMagixEndpoint +import space.kscince.magix.zmq.ZmqMagixFlowPlugin import java.awt.Desktop import java.net.URI @@ -32,7 +32,7 @@ internal const val numberOfMessages = 100 suspend fun main(): Unit = coroutineScope { val logger = LoggerFactory.getLogger("magix-demo") logger.info("Starting magix server") - val server = startMagixServer(RSocketMagix(), ZmqMagix(), buffer = 10) + val server = startMagixServer(RSocketMagixFlowPlugin(), ZmqMagixFlowPlugin(), buffer = 10) server.apply { val host = "localhost"//environment.connectors.first().host diff --git a/magix/magix-server/build.gradle.kts b/magix/magix-server/build.gradle.kts index dc7cb28..4a3b102 100644 --- a/magix/magix-server/build.gradle.kts +++ b/magix/magix-server/build.gradle.kts @@ -28,6 +28,4 @@ dependencies{ api("io.rsocket.kotlin:rsocket-ktor-server:$rsocketVersion") api("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") - - api("org.zeromq:jeromq:0.5.2") } \ No newline at end of file diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagix.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt similarity index 96% rename from magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagix.kt rename to magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt index 8deafdf..a2f875d 100644 --- a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagix.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt @@ -18,7 +18,7 @@ import space.kscience.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_RAW_PORT /** * Raw TCP magix server */ -public class RSocketMagix(public val port: Int = DEFAULT_MAGIX_RAW_PORT): MagixFlowPlugin { +public class RSocketMagixFlowPlugin(public val port: Int = DEFAULT_MAGIX_RAW_PORT): MagixFlowPlugin { override fun start(scope: CoroutineScope, magixFlow: MutableSharedFlow): Job { val tcpTransport = TcpServerTransport(port = port) val rSocketJob: TcpServer = RSocketServer().bindIn(scope, tcpTransport, acceptor(scope, magixFlow)) diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt index e9bedd3..e5cb6cb 100644 --- a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt @@ -105,7 +105,7 @@ public fun Application.magixModule(magixFlow: MutableSharedFlow, r magixFlow.emit(message) } //rSocket server. Filter from Payload - rSocket("rsocket", acceptor = RSocketMagix.acceptor( application, magixFlow)) + rSocket("rsocket", acceptor = RSocketMagixFlowPlugin.acceptor( application, magixFlow)) } } } diff --git a/magix/magix-zmq/build.gradle.kts b/magix/magix-zmq/build.gradle.kts index b47b568..e7c9f77 100644 --- a/magix/magix-zmq/build.gradle.kts +++ b/magix/magix-zmq/build.gradle.kts @@ -9,5 +9,6 @@ description = """ dependencies { api(projects.magix.magixApi) + api("org.slf4j:slf4j-api:2.0.6") implementation("org.zeromq:jeromq:0.5.2") } diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/ZmqMagix.kt b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt similarity index 96% rename from magix/magix-server/src/main/kotlin/space/kscience/magix/server/ZmqMagix.kt rename to magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt index c2e54a0..1f075ac 100644 --- a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/ZmqMagix.kt +++ b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt @@ -1,4 +1,4 @@ -package space.kscience.magix.server +package space.kscince.magix.zmq import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableSharedFlow @@ -14,7 +14,7 @@ import space.kscience.magix.api.MagixFlowPlugin import space.kscience.magix.api.MagixMessage -public class ZmqMagix( +public class ZmqMagixFlowPlugin( public val localHost: String = "tcp://*", public val zmqPubSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT, public val zmqPullSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, From 3ef471d49ee348d17924ef6d0d901582df6f7ed3 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 26 Feb 2023 20:23:30 +0300 Subject: [PATCH 71/74] Move ZMQ to a separate module --- .../kscience/controls/opcua/server/DeviceNameSpace.kt | 3 +++ .../space/kscience/controls/demo/DemoControllerView.kt | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt index 84537ef..6b8e44c 100644 --- a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt @@ -208,5 +208,8 @@ public class DeviceNameSpace( } } +/** + * Serve devices from [deviceManager] as OPC-UA + */ public fun OpcUaServer.serveDevices(deviceManager: DeviceManager): DeviceNameSpace = DeviceNameSpace(this, deviceManager).apply { startup() } \ No newline at end of file diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt index 4fe6798..802fc4d 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt @@ -51,13 +51,18 @@ class DemoController : Controller(), ContextAware { context.launch { device = deviceManager.install("demo", DemoDevice) //starting magix event loop - magixServer = startMagixServer(RSocketMagixFlowPlugin(), ZmqMagixFlowPlugin()) + magixServer = startMagixServer( + RSocketMagixFlowPlugin(), //TCP rsocket support + ZmqMagixFlowPlugin() //ZMQ support + ) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost") deviceManager.connectToMagix(deviceEndpoint) + //connect visualization to a magix endpoint val visualEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost") visualizer = visualEndpoint.startDemoDeviceServer() + //serve devices as OPC-UA namespace opcUaServer.startup() opcUaServer.serveDevices(deviceManager) } From 5b16b07172c8c298b16ed40a20cfd5a9354aec80 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 26 Feb 2023 22:07:19 +0300 Subject: [PATCH 72/74] Fix build --- build.gradle.kts | 3 +-- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index ecac2af..c9162f9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,8 +15,7 @@ allprojects { group = "space.kscience" version = "0.1.1-SNAPSHOT" repositories{ - mavenCentral() - mavenLocal() + maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") } } diff --git a/gradle.properties b/gradle.properties index 05b0759..d422be3 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,4 +7,4 @@ org.gradle.parallel=true publishing.github=false publishing.sonatype=false -toolsVersion=0.14.2-kotlin-1.8.10 \ No newline at end of file +toolsVersion=0.14.3-kotlin-1.8.10 \ No newline at end of file From ba84553be51b74ad7fa3200c8ec2b4014218df3b Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 1 Mar 2023 20:59:18 +0300 Subject: [PATCH 73/74] Add port demo. Nullable properties --- .../space/kscience/controls/api/Device.kt | 4 +- .../space/kscience/controls/api/Socket.kt | 2 +- .../space/kscience/controls/ports/Port.kt | 16 ++- .../space/kscience/controls/ports/Ports.kt | 34 ++++++ ...onousPortHandler.kt => SynchronousPort.kt} | 34 ++++-- .../space/kscience/controls/ports/phrases.kt | 5 +- .../kscience/controls/spec/DeviceBase.kt | 35 ++++-- .../controls/spec/DevicePropertySpec.kt | 6 +- .../kscience/controls/spec/DeviceSpec.kt | 115 ++++++++++++------ .../controls/spec/propertySpecDelegates.kt | 12 +- .../space/kscience/controls/ports/TcpPort.kt | 9 +- .../kscience/controls/ports/TcpPortPlugin.kt | 30 +++++ .../controls/spec/getDeviceProperty.kt | 2 +- .../build.gradle.kts | 2 +- .../kscience/controls/ports/KtorTcpPort.kt | 5 +- .../controls/ports/KtorTcpPortPlugin.kt | 30 +++++ .../kscience/controls/serial/SerialPort.kt | 3 + .../controls/serial/SerialPortPlugin.kt | 31 +++++ controls-server/build.gradle.kts | 2 +- demo/mks-pdr900/build.gradle.kts | 21 ++++ .../sciprog/devices/mks/MksPdr900Device.kt | 102 ++++++++++++++++ .../mks/NullableStringMetaConverter.kt | 10 ++ demo/motors/build.gradle.kts | 2 +- .../pimotionmaster/PiMotionMasterApp.kt | 4 +- .../pimotionmaster/PiMotionMasterDevice.kt | 4 +- settings.gradle.kts | 5 +- 26 files changed, 431 insertions(+), 94 deletions(-) create mode 100644 controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Ports.kt rename controls-core/src/commonMain/kotlin/space/kscience/controls/ports/{SynchronousPortHandler.kt => SynchronousPort.kt} (50%) create mode 100644 controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPortPlugin.kt rename {controls-tcp => controls-ktor-tcp}/build.gradle.kts (81%) rename {controls-tcp => controls-ktor-tcp}/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt (96%) create mode 100644 controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPortPlugin.kt create mode 100644 controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPortPlugin.kt create mode 100644 demo/mks-pdr900/build.gradle.kts create mode 100644 demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/MksPdr900Device.kt create mode 100644 demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/NullableStringMetaConverter.kt diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Device.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Device.kt index 223bd0c..c122260 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Device.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Device.kt @@ -57,7 +57,7 @@ public interface Device : Closeable, ContextAware, CoroutineScope { /** * Set property [value] for a property with name [propertyName]. - * In rare cases could suspend if the [Device] supports command queue and it is full at the moment. + * In rare cases could suspend if the [Device] supports command queue, and it is full at the moment. */ public suspend fun writeProperty(propertyName: String, value: Meta) @@ -101,7 +101,7 @@ public suspend fun Device.getOrReadProperty(propertyName: String): Meta = * * TODO currently this */ -public fun Device.getProperties(): Meta = Meta { +public fun Device.getAllProperties(): Meta = Meta { for (descriptor in propertyDescriptors) { setMeta(Name.parse(descriptor.name), getProperty(descriptor.name)) } diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Socket.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Socket.kt index 4d1bb1e..02598ba 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Socket.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Socket.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.launch /** - * A generic bi-directional sender/receiver object + * A generic bidirectional sender/receiver object */ public interface Socket : Closeable { /** diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt index e7f9504..374c404 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt @@ -6,11 +6,19 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.receiveAsFlow import space.kscience.controls.api.Socket import space.kscience.dataforge.context.* +import space.kscience.dataforge.misc.Type import kotlin.coroutines.CoroutineContext public interface Port : ContextAware, Socket -public typealias PortFactory = Factory +@Type(PortFactory.TYPE) +public interface PortFactory: Factory{ + public val type: String + + public companion object{ + public const val TYPE: String = "controls.port" + } +} public abstract class AbstractPort( override val context: Context, @@ -64,12 +72,10 @@ public abstract class AbstractPort( /** * Raw flow of incoming data chunks. The chunks are not guaranteed to be complete phrases. - * In order to form phrases some condition should used on top of it. + * In order to form phrases some condition should be used on top of it. * For example [delimitedIncoming] generates phrases with fixed delimiter. */ - override fun receiving(): Flow { - return incoming.receiveAsFlow() - } + override fun receiving(): Flow = incoming.receiveAsFlow() override fun close() { outgoing.close() diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Ports.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Ports.kt new file mode 100644 index 0000000..8c652cc --- /dev/null +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Ports.kt @@ -0,0 +1,34 @@ +package space.kscience.controls.ports + +import space.kscience.dataforge.context.* +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.string +import kotlin.reflect.KClass + +public class Ports : AbstractPlugin() { + + override val tag: PluginTag get() = Companion.tag + + private val portFactories by lazy { + context.gather(PortFactory.TYPE) + } + + private val portCache = mutableMapOf() + + public fun buildPort(meta: Meta): Port = portCache.getOrPut(meta) { + val type by meta.string { error("Port type is not defined") } + val factory = portFactories.values.firstOrNull { it.type == type } + ?: error("Port factory for type $type not found") + factory.build(context, meta) + } + + public companion object : PluginFactory { + + override val tag: PluginTag = PluginTag("controls.ports", group = PluginTag.DATAFORGE_GROUP) + + override val type: KClass = Ports::class + + override fun build(context: Context, meta: Meta): Ports = Ports() + + } +} \ No newline at end of file diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/SynchronousPortHandler.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/SynchronousPort.kt similarity index 50% rename from controls-core/src/commonMain/kotlin/space/kscience/controls/ports/SynchronousPortHandler.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/ports/SynchronousPort.kt index 6dd8849..0ed4764 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/SynchronousPortHandler.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/SynchronousPort.kt @@ -8,27 +8,35 @@ import kotlinx.coroutines.sync.withLock /** * A port handler for synchronous (request-response) communication with a port. Only one request could be active at a time (others are suspended. * The handler does not guarantee exclusive access to the port so the user mush ensure that no other controller handles port at the moment. - * */ -public class SynchronousPortHandler(public val port: Port) { - private val mutex = Mutex() - +public class SynchronousPort(public val port: Port, private val mutex: Mutex) : Port by port { /** * Send a single message and wait for the flow of respond messages. */ - public suspend fun respond(data: ByteArray, transform: suspend Flow.() -> R): R { - return mutex.withLock { - port.send(data) - transform(port.receiving()) - } + public suspend fun respond(data: ByteArray, transform: suspend Flow.() -> R): R = mutex.withLock { + port.send(data) + transform(port.receiving()) } } +/** + * Provide a synchronous wrapper for a port + */ +public fun Port.synchronous(mutex: Mutex = Mutex()): SynchronousPort = SynchronousPort(this, mutex) + /** * Send request and read incoming data blocks until the delimiter is encountered */ -public suspend fun SynchronousPortHandler.respondWithDelimiter(data: ByteArray, delimiter: ByteArray): ByteArray { - return respond(data) { - withDelimiter(delimiter).first() - } +public suspend fun SynchronousPort.respondWithDelimiter( + data: ByteArray, + delimiter: ByteArray, +): ByteArray = respond(data) { + withDelimiter(delimiter).first() +} + +public suspend fun SynchronousPort.respondStringWithDelimiter( + data: String, + delimiter: String, +): String = respond(data.encodeToByteArray()) { + withStringDelimiter(delimiter).first() } \ No newline at end of file diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt index 8543825..21afa8d 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt @@ -40,12 +40,11 @@ public fun Flow.withDelimiter(delimiter: ByteArray): Flow /** * Transform byte fragments into utf-8 phrases using utf-8 delimiter */ -public fun Flow.withDelimiter(delimiter: String): Flow { +public fun Flow.withStringDelimiter(delimiter: String): Flow { return withDelimiter(delimiter.encodeToByteArray()).map { it.decodeToString() } } /** * A flow of delimited phrases */ -public suspend fun Port.delimitedIncoming(delimiter: ByteArray): Flow = - receiving().withDelimiter(delimiter) +public fun Port.delimitedIncoming(delimiter: ByteArray): Flow = receiving().withDelimiter(delimiter) diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt index 4c114b7..d1fd9b7 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt @@ -17,10 +17,17 @@ import kotlin.coroutines.CoroutineContext @OptIn(InternalDeviceAPI::class) public abstract class DeviceBase>( override val context: Context = Global, - override val meta: Meta = Meta.EMPTY + override val meta: Meta = Meta.EMPTY, ) : Device { + /** + * Collection of property specifications + */ public abstract val properties: Map> + + /** + * Collection of action specifications + */ public abstract val actions: Map> override val propertyDescriptors: Collection @@ -33,6 +40,9 @@ public abstract class DeviceBase>( context.coroutineContext + SupervisorJob(context.coroutineContext[Job]) } + /** + * Logical state store + */ private val logicalState: HashMap = HashMap() private val sharedMessageFlow: MutableSharedFlow = MutableSharedFlow() @@ -99,14 +109,18 @@ public abstract class DeviceBase>( actions[action]?.executeWithMeta(self, argument) /** - * Read typed value and update/push event if needed + * Read typed value and update/push event if needed. + * Return null if property read is not successful or property is undefined. */ - public suspend fun DevicePropertySpec.read(): T { - val res = read(self) + public suspend fun DevicePropertySpec.readOrNull(): T? { + val res = read(self) ?: return null updateLogical(name, converter.objectToMeta(res)) return res } + public suspend fun DevicePropertySpec.read(): T = + readOrNull() ?: error("Failed to read property $name state") + public fun DevicePropertySpec.get(): T? = getProperty(name)?.let(converter::metaToObject) /** @@ -121,6 +135,13 @@ public abstract class DeviceBase>( } } + /** + * Reset logical state of a property + */ + public suspend fun DevicePropertySpec.invalidate() { + invalidate(name) + } + public suspend operator fun DeviceActionSpec.invoke(input: I? = null): O? = execute(self, input) } @@ -132,17 +153,17 @@ public abstract class DeviceBase>( public open class DeviceBySpec>( public val spec: DeviceSpec, context: Context = Global, - meta: Meta = Meta.EMPTY + meta: Meta = Meta.EMPTY, ) : DeviceBase(context, meta) { override val properties: Map> get() = spec.properties override val actions: Map> get() = spec.actions - override suspend fun open(): Unit = with(spec){ + override suspend fun open(): Unit = with(spec) { super.open() self.onOpen() } - override fun close(): Unit = with(spec){ + override fun close(): Unit = with(spec) { self.onClose() super.close() } diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DevicePropertySpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DevicePropertySpec.kt index c1bdc11..b545910 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DevicePropertySpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DevicePropertySpec.kt @@ -35,7 +35,7 @@ public interface DevicePropertySpec { * Read physical value from the given [device] */ @InternalDeviceAPI - public suspend fun read(device: D): T + public suspend fun read(device: D): T? } /** @@ -44,8 +44,8 @@ public interface DevicePropertySpec { public val DevicePropertySpec<*, *>.name: String get() = descriptor.name @OptIn(InternalDeviceAPI::class) -public suspend fun DevicePropertySpec.readMeta(device: D): Meta = - converter.objectToMeta(read(device)) +public suspend fun DevicePropertySpec.readMeta(device: D): Meta? = + read(device)?.let(converter::objectToMeta) public interface WritableDevicePropertySpec : DevicePropertySpec { diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt index 1cf567c..00f5ad5 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt @@ -24,25 +24,26 @@ public abstract class DeviceSpec { public val actions: Map> get() = _actions - public open suspend fun D.onOpen(){ + public open suspend fun D.onOpen() { } - public open fun D.onClose(){ + public open fun D.onClose() { } - public fun > registerProperty(deviceProperty: P): P { + public fun > registerProperty(deviceProperty: P): P { _properties[deviceProperty.name] = deviceProperty return deviceProperty } - public fun registerProperty( + public fun registerProperty( converter: MetaConverter, readOnlyProperty: KProperty1, - descriptorBuilder: PropertyDescriptor.() -> Unit = {} + descriptorBuilder: PropertyDescriptor.() -> Unit = {}, ): DevicePropertySpec { val deviceProperty = object : DevicePropertySpec { - override val descriptor: PropertyDescriptor = PropertyDescriptor(readOnlyProperty.name).apply(descriptorBuilder) + override val descriptor: PropertyDescriptor = + PropertyDescriptor(readOnlyProperty.name).apply(descriptorBuilder) override val converter: MetaConverter = converter override suspend fun read(device: D): T = withContext(device.coroutineContext) { readOnlyProperty.get(device) } @@ -50,11 +51,11 @@ public abstract class DeviceSpec { return registerProperty(deviceProperty) } - public fun property( + public fun property( converter: MetaConverter, readOnlyProperty: KProperty1, - descriptorBuilder: PropertyDescriptor.() -> Unit = {} - ): PropertyDelegateProvider, ReadOnlyProperty>> = + descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + ): PropertyDelegateProvider, ReadOnlyProperty>> = PropertyDelegateProvider { _, property -> val deviceProperty = object : DevicePropertySpec { override val descriptor: PropertyDescriptor = PropertyDescriptor(property.name).apply { @@ -74,10 +75,10 @@ public abstract class DeviceSpec { } } - public fun mutableProperty( + public fun mutableProperty( converter: MetaConverter, readWriteProperty: KMutableProperty1, - descriptorBuilder: PropertyDescriptor.() -> Unit = {} + descriptorBuilder: PropertyDescriptor.() -> Unit = {}, ): PropertyDelegateProvider, ReadOnlyProperty>> = PropertyDelegateProvider { _, property -> val deviceProperty = object : WritableDevicePropertySpec { @@ -103,11 +104,11 @@ public abstract class DeviceSpec { } } - public fun property( + public fun property( converter: MetaConverter, descriptorBuilder: PropertyDescriptor.() -> Unit = {}, name: String? = null, - read: suspend D.() -> T + read: suspend D.() -> T?, ): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> = PropertyDelegateProvider { _: DeviceSpec, property -> val propertyName = name ?: property.name @@ -115,7 +116,7 @@ public abstract class DeviceSpec { override val descriptor: PropertyDescriptor = PropertyDescriptor(propertyName).apply(descriptorBuilder) override val converter: MetaConverter = converter - override suspend fun read(device: D): T = withContext(device.coroutineContext) { device.read() } + override suspend fun read(device: D): T? = withContext(device.coroutineContext) { device.read() } } registerProperty(deviceProperty) ReadOnlyProperty, DevicePropertySpec> { _, _ -> @@ -123,12 +124,12 @@ public abstract class DeviceSpec { } } - public fun mutableProperty( + public fun mutableProperty( converter: MetaConverter, descriptorBuilder: PropertyDescriptor.() -> Unit = {}, name: String? = null, - read: suspend D.() -> T, - write: suspend D.(T) -> Unit + read: suspend D.() -> T?, + write: suspend D.(T) -> Unit, ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = PropertyDelegateProvider { _: DeviceSpec, property: KProperty<*> -> val propertyName = name ?: property.name @@ -136,7 +137,7 @@ public abstract class DeviceSpec { override val descriptor: PropertyDescriptor = PropertyDescriptor(propertyName).apply(descriptorBuilder) override val converter: MetaConverter = converter - override suspend fun read(device: D): T = withContext(device.coroutineContext) { device.read() } + override suspend fun read(device: D): T? = withContext(device.coroutineContext) { device.read() } override suspend fun write(device: D, value: T): Unit = withContext(device.coroutineContext) { device.write(value) @@ -149,17 +150,17 @@ public abstract class DeviceSpec { } - public fun registerAction(deviceAction: DeviceActionSpec): DeviceActionSpec { + public fun registerAction(deviceAction: DeviceActionSpec): DeviceActionSpec { _actions[deviceAction.name] = deviceAction return deviceAction } - public fun action( + public fun action( inputConverter: MetaConverter, outputConverter: MetaConverter, descriptorBuilder: ActionDescriptor.() -> Unit = {}, name: String? = null, - execute: suspend D.(I?) -> O? + execute: suspend D.(I?) -> O?, ): PropertyDelegateProvider, ReadOnlyProperty, DeviceActionSpec>> = PropertyDelegateProvider { _: DeviceSpec, property -> val actionName = name ?: property.name @@ -185,15 +186,16 @@ public abstract class DeviceSpec { public fun metaAction( descriptorBuilder: ActionDescriptor.() -> Unit = {}, name: String? = null, - execute: suspend D.(Meta?) -> Meta? - ): PropertyDelegateProvider, ReadOnlyProperty, DeviceActionSpec>> = action( - MetaConverter.Companion.meta, - MetaConverter.Companion.meta, - descriptorBuilder, - name - ){ - execute(it) - } + execute: suspend D.(Meta?) -> Meta?, + ): PropertyDelegateProvider, ReadOnlyProperty, DeviceActionSpec>> = + action( + MetaConverter.Companion.meta, + MetaConverter.Companion.meta, + descriptorBuilder, + name + ) { + execute(it) + } /** * An action that takes no parameters and returns no values @@ -201,14 +203,47 @@ public abstract class DeviceSpec { public fun unitAction( descriptorBuilder: ActionDescriptor.() -> Unit = {}, name: String? = null, - execute: suspend D.() -> Unit - ): PropertyDelegateProvider, ReadOnlyProperty, DeviceActionSpec>> = action( - MetaConverter.Companion.meta, - MetaConverter.Companion.meta, - descriptorBuilder, - name - ){ - execute() - null - } + execute: suspend D.() -> Unit, + ): PropertyDelegateProvider, ReadOnlyProperty, DeviceActionSpec>> = + action( + MetaConverter.Companion.meta, + MetaConverter.Companion.meta, + descriptorBuilder, + name + ) { + execute() + null + } } + + +/** + * Register a mutable logical property for a device + */ +@OptIn(InternalDeviceAPI::class) +public fun > DeviceSpec.logicalProperty( + converter: MetaConverter, + descriptorBuilder: PropertyDescriptor.() -> Unit = {}, + name: String? = null, +): PropertyDelegateProvider, ReadOnlyProperty>> = + PropertyDelegateProvider { _, property -> + val deviceProperty = object : WritableDevicePropertySpec { + val propertyName = name ?: property.name + override val descriptor: PropertyDescriptor = PropertyDescriptor(propertyName).apply { + //TODO add type from converter + writable = true + }.apply(descriptorBuilder) + + override val converter: MetaConverter = converter + + override suspend fun read(device: D): T? = + device.getProperty(propertyName)?.let(converter::metaToObject) + + override suspend fun write(device: D, value: T): Unit = + device.writeProperty(propertyName, converter.objectToMeta(value)) + } + registerProperty(deviceProperty) + ReadOnlyProperty { _, _ -> + deviceProperty + } + } \ No newline at end of file diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt index 315b942..e2afcab 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/propertySpecDelegates.kt @@ -13,7 +13,7 @@ import kotlin.properties.ReadOnlyProperty public fun > DeviceSpec.booleanProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, name: String? = null, - read: suspend D.() -> Boolean + read: suspend D.() -> Boolean? ): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> = property( MetaConverter.boolean, { @@ -38,7 +38,7 @@ private inline fun numberDescriptor( public fun > DeviceSpec.numberProperty( name: String? = null, descriptorBuilder: PropertyDescriptor.() -> Unit = {}, - read: suspend D.() -> Number + read: suspend D.() -> Number? ): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> = property( MetaConverter.number, numberDescriptor(descriptorBuilder), @@ -49,7 +49,7 @@ public fun > DeviceSpec.numberProperty( public fun > DeviceSpec.doubleProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, name: String? = null, - read: suspend D.() -> Double + read: suspend D.() -> Double? ): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> = property( MetaConverter.double, numberDescriptor(descriptorBuilder), @@ -60,7 +60,7 @@ public fun > DeviceSpec.doubleProperty( public fun > DeviceSpec.stringProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, name: String? = null, - read: suspend D.() -> String + read: suspend D.() -> String? ): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> = property( MetaConverter.string, { @@ -76,7 +76,7 @@ public fun > DeviceSpec.stringProperty( public fun > DeviceSpec.metaProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, name: String? = null, - read: suspend D.() -> Meta + read: suspend D.() -> Meta? ): PropertyDelegateProvider, ReadOnlyProperty, DevicePropertySpec>> = property( MetaConverter.meta, { @@ -94,7 +94,7 @@ public fun > DeviceSpec.metaProperty( public fun > DeviceSpec.booleanProperty( descriptorBuilder: PropertyDescriptor.() -> Unit = {}, name: String? = null, - read: suspend D.() -> Boolean, + read: suspend D.() -> Boolean?, write: suspend D.(Boolean) -> Unit ): PropertyDelegateProvider, ReadOnlyProperty, WritableDevicePropertySpec>> = mutableProperty( diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt index d458c4d..77fec44 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt @@ -52,20 +52,20 @@ public class TcpPort private constructor( } if (num < 0) cancel("The input channel is exhausted") } catch (ex: Exception) { - logger.error(ex){"Channel read error"} + logger.error(ex) { "Channel read error" } delay(1000) } } } - override suspend fun write(data: ByteArray) { + override suspend fun write(data: ByteArray): Unit = withContext(Dispatchers.IO){ futureChannel.await().write(ByteBuffer.wrap(data)) } @OptIn(ExperimentalCoroutinesApi::class) override fun close() { listenerJob.cancel() - if(futureChannel.isCompleted){ + if (futureChannel.isCompleted) { futureChannel.getCompleted().close() } else { futureChannel.cancel() @@ -74,6 +74,9 @@ public class TcpPort private constructor( } public companion object : PortFactory { + + override val type: String = "tcp" + public fun open( context: Context, host: String, diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPortPlugin.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPortPlugin.kt new file mode 100644 index 0000000..23174af --- /dev/null +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPortPlugin.kt @@ -0,0 +1,30 @@ +package space.kscience.controls.ports + +import space.kscience.dataforge.context.AbstractPlugin +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.dataforge.names.Name +import kotlin.reflect.KClass + +public class TcpPortPlugin : AbstractPlugin() { + + override val tag: PluginTag get() = Companion.tag + + override fun content(target: String): Map = when(target){ + PortFactory.TYPE -> mapOf(Name.EMPTY to TcpPort) + else -> emptyMap() + } + + public companion object : PluginFactory { + + override val tag: PluginTag = PluginTag("controls.ports.tcp", group = PluginTag.DATAFORGE_GROUP) + + override val type: KClass = TcpPortPlugin::class + + override fun build(context: Context, meta: Meta): TcpPortPlugin = TcpPortPlugin() + + } + +} \ No newline at end of file diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/spec/getDeviceProperty.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/spec/getDeviceProperty.kt index 4a783e2..a7063f5 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/spec/getDeviceProperty.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/spec/getDeviceProperty.kt @@ -7,4 +7,4 @@ import kotlinx.coroutines.runBlocking */ public operator fun , T : Any> D.get( propertySpec: DevicePropertySpec -): T = runBlocking { read(propertySpec) } \ No newline at end of file +): T? = runBlocking { read(propertySpec) } \ No newline at end of file diff --git a/controls-tcp/build.gradle.kts b/controls-ktor-tcp/build.gradle.kts similarity index 81% rename from controls-tcp/build.gradle.kts rename to controls-ktor-tcp/build.gradle.kts index c15c892..2089b19 100644 --- a/controls-tcp/build.gradle.kts +++ b/controls-ktor-tcp/build.gradle.kts @@ -5,6 +5,6 @@ plugins { val ktorVersion: String by rootProject.extra dependencies { - api(project(":controls-core")) + api(projects.controlsCore) api("io.ktor:ktor-network:$ktorVersion") } diff --git a/controls-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt b/controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt similarity index 96% rename from controls-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt rename to controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt index 9f652b2..7f906d3 100644 --- a/controls-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt +++ b/controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt @@ -55,7 +55,10 @@ public class KtorTcpPort internal constructor( super.close() } - public companion object: PortFactory { + public companion object : PortFactory { + + override val type: String = "tcp" + public fun open( context: Context, host: String, diff --git a/controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPortPlugin.kt b/controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPortPlugin.kt new file mode 100644 index 0000000..1256455 --- /dev/null +++ b/controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPortPlugin.kt @@ -0,0 +1,30 @@ +package space.kscience.controls.ports + +import space.kscience.dataforge.context.AbstractPlugin +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.dataforge.names.Name +import kotlin.reflect.KClass + +public class KtorTcpPortPlugin : AbstractPlugin() { + + override val tag: PluginTag get() = Companion.tag + + override fun content(target: String): Map = when(target){ + PortFactory.TYPE -> mapOf(Name.EMPTY to KtorTcpPort) + else -> emptyMap() + } + + public companion object : PluginFactory { + + override val tag: PluginTag = PluginTag("controls.ports.serial", group = PluginTag.DATAFORGE_GROUP) + + override val type: KClass = KtorTcpPortPlugin::class + + override fun build(context: Context, meta: Meta): KtorTcpPortPlugin = KtorTcpPortPlugin() + + } + +} \ No newline at end of file diff --git a/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt index 737e90d..05d4b64 100644 --- a/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt +++ b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt @@ -58,6 +58,9 @@ public class SerialPort private constructor( public companion object : PortFactory { + override val type: String = "com" + + /** * Construct ComPort with given parameters */ diff --git a/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPortPlugin.kt b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPortPlugin.kt new file mode 100644 index 0000000..871d266 --- /dev/null +++ b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPortPlugin.kt @@ -0,0 +1,31 @@ +package space.kscience.controls.serial + +import space.kscience.controls.ports.PortFactory +import space.kscience.dataforge.context.AbstractPlugin +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.dataforge.names.Name +import kotlin.reflect.KClass + +public class SerialPortPlugin : AbstractPlugin() { + + override val tag: PluginTag get() = Companion.tag + + override fun content(target: String): Map = when(target){ + PortFactory.TYPE -> mapOf(Name.EMPTY to SerialPort) + else -> emptyMap() + } + + public companion object : PluginFactory { + + override val tag: PluginTag = PluginTag("controls.ports.serial", group = PluginTag.DATAFORGE_GROUP) + + override val type: KClass = SerialPortPlugin::class + + override fun build(context: Context, meta: Meta): SerialPortPlugin = SerialPortPlugin() + + } + +} \ No newline at end of file diff --git a/controls-server/build.gradle.kts b/controls-server/build.gradle.kts index e9471a5..0553b72 100644 --- a/controls-server/build.gradle.kts +++ b/controls-server/build.gradle.kts @@ -12,7 +12,7 @@ val ktorVersion: String by rootProject.extra dependencies { implementation(project(":controls-core")) - implementation(project(":controls-tcp")) + implementation(project(":controls-ktor-tcp")) implementation(projects.magix.magixServer) implementation("io.ktor:ktor-server-cio:$ktorVersion") implementation("io.ktor:ktor-server-websockets:$ktorVersion") diff --git a/demo/mks-pdr900/build.gradle.kts b/demo/mks-pdr900/build.gradle.kts new file mode 100644 index 0000000..bae6263 --- /dev/null +++ b/demo/mks-pdr900/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + id("space.kscience.gradle.jvm") + application +} + +//TODO to be moved to a separate project +// +//application{ +// mainClass.set("ru.mipt.npm.devices.pimotionmaster.PiMotionMasterAppKt") +//} + +kotlin{ + explicitApi = null +} + +val ktorVersion: String by rootProject.extra +val dataforgeVersion: String by extra + +dependencies { + implementation(projects.controlsKtorTcp) +} diff --git a/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/MksPdr900Device.kt b/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/MksPdr900Device.kt new file mode 100644 index 0000000..7ba1e03 --- /dev/null +++ b/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/MksPdr900Device.kt @@ -0,0 +1,102 @@ +package center.sciprog.devices.mks + +import kotlinx.coroutines.withTimeoutOrNull +import space.kscience.controls.ports.Ports +import space.kscience.controls.ports.SynchronousPort +import space.kscience.controls.ports.respondStringWithDelimiter +import space.kscience.controls.ports.synchronous +import space.kscience.controls.spec.* +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.Factory +import space.kscience.dataforge.context.request +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.int +import space.kscience.dataforge.meta.transformations.MetaConverter + +class MksPdr900Device(context: Context, meta: Meta) : DeviceBySpec(MksPdr900Device, context, meta) { + + private val portDelegate = lazy { + val ports = context.request(Ports) + ports.buildPort(meta["port"] ?: error("Port is not defined in device configuration")).synchronous() + } + + private val port: SynchronousPort by portDelegate + + private val address by meta.int(253) + private val channel by meta.int(5) + + private val responsePattern: Regex by lazy { + ("@${address}ACK(.*);FF").toRegex() + } + + private suspend fun talk(requestContent: String): String? = withTimeoutOrNull(5000) { + val answer = port.respondStringWithDelimiter(String.format("@%s%s;FF", address, requestContent), ";FF") + responsePattern.matchEntire(answer)?.groups?.get(1)?.value + ?: error("Message $answer does not match $responsePattern") + } + + public suspend fun readPowerOn(): Boolean = when (val answer = talk("FP?")) { + "ON" -> true + "OFF" -> false + else -> error("Unknown answer for 'FP?': $answer") + } + + + public suspend fun writePowerOn(powerOnValue: Boolean) { + error.invalidate() + if (powerOnValue) { + val ans = talk("FP!ON") + if (ans == "ON") { + updateLogical(powerOn, true) + } else { + updateLogical(error, "Failed to set power state") + } + } else { + val ans = talk("FP!OFF") + if (ans == "OFF") { + updateLogical(powerOn, false) + } else { + updateLogical(error, "Failed to set power state") + } + } + } + + public suspend fun readChannelData(): Double? { + val answer: String? = talk("PR$channel?") + error.invalidate() + return if (answer.isNullOrEmpty()) { + // updateState(PortSensor.CONNECTED_STATE, false) + updateLogical(error, "No connection") + null + } else { + val res = answer.toDouble() + if (res <= 0) { + updateLogical(powerOn, false) + updateLogical(error, "No power") + null + } else { + res + } + } + } + + + companion object : DeviceSpec(), Factory { + override fun build(context: Context, meta: Meta): MksPdr900Device = MksPdr900Device(context, meta) + + + val powerOn by booleanProperty(read = MksPdr900Device::readPowerOn, write = MksPdr900Device::writePowerOn) + + val value by doubleProperty(read = MksPdr900Device::readChannelData) + + val error by logicalProperty(MetaConverter.string) + + + override fun MksPdr900Device.onClose() { + if (portDelegate.isInitialized()) { + port.close() + } + } + } +} diff --git a/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/NullableStringMetaConverter.kt b/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/NullableStringMetaConverter.kt new file mode 100644 index 0000000..40c20ed --- /dev/null +++ b/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/NullableStringMetaConverter.kt @@ -0,0 +1,10 @@ +package center.sciprog.devices.mks + +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.string +import space.kscience.dataforge.meta.transformations.MetaConverter + +object NullableStringMetaConverter : MetaConverter { + override fun metaToObject(meta: Meta): String? = meta.string + override fun objectToMeta(obj: String?): Meta = Meta {} +} \ No newline at end of file diff --git a/demo/motors/build.gradle.kts b/demo/motors/build.gradle.kts index 04f0d72..b60ebb9 100644 --- a/demo/motors/build.gradle.kts +++ b/demo/motors/build.gradle.kts @@ -23,7 +23,7 @@ val ktorVersion: String by rootProject.extra val dataforgeVersion: String by extra dependencies { - implementation(project(":controls-tcp")) + implementation(project(":controls-ktor-tcp")) implementation(project(":controls-magix-client")) implementation("no.tornado:tornadofx:1.7.20") } diff --git a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt index 37f7323..45fe757 100644 --- a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt +++ b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp.kt @@ -44,8 +44,8 @@ fun VBox.piMotionMasterAxis( label(axisName) coroutineScope.launch { with(axis) { - val min = minPosition.read() - val max = maxPosition.read() + val min: Double = minPosition.read() + val max: Double = maxPosition.read() val positionProperty = fxProperty(position) val startPosition = position.read() runLater { diff --git a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt index 49404f0..3b00e0c 100644 --- a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt +++ b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt @@ -87,7 +87,7 @@ class PiMotionMasterDevice( suspend fun getErrorCode(): Int = mutex.withLock { withTimeout(timeoutValue) { sendCommandInternal("ERR?") - val errorString = port?.receiving()?.withDelimiter("\n")?.first() ?: error("Not connected to device") + val errorString = port?.receiving()?.withStringDelimiter("\n")?.first() ?: error("Not connected to device") errorString.trim().toInt() } } @@ -100,7 +100,7 @@ class PiMotionMasterDevice( try { withTimeout(timeoutValue) { sendCommandInternal(command, *arguments) - val phrases = port?.receiving()?.withDelimiter("\n") ?: error("Not connected to device") + val phrases = port?.receiving()?.withStringDelimiter("\n") ?: error("Not connected to device") phrases.transformWhile { line -> emit(line) line.endsWith(" \n") diff --git a/settings.gradle.kts b/settings.gradle.kts index de36d30..a8f250b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -42,7 +42,7 @@ dependencyResolutionManagement { include( ":controls-core", - ":controls-tcp", + ":controls-ktor-tcp", ":controls-serial", ":controls-server", ":controls-opcua", @@ -64,5 +64,6 @@ include( ":demo:magix-demo", ":demo:car", ":demo:motors", - ":demo:echo" + ":demo:echo", + ":demo:mks-pdr900" ) From 00c66da847b7b1b899dcef74875c51874707be18 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 2 Mar 2023 10:07:54 +0300 Subject: [PATCH 74/74] Update pressure sensor demo --- .../kscience/controls/spec/DeviceSpec.kt | 3 +-- .../sciprog/devices/mks/MksPdr900Device.kt | 19 +++++++++++++------ .../pimotionmaster/PiMotionMasterDevice.kt | 4 ++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt index 00f5ad5..83364ee 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt @@ -236,8 +236,7 @@ public fun > DeviceSpec.logicalProperty( override val converter: MetaConverter = converter - override suspend fun read(device: D): T? = - device.getProperty(propertyName)?.let(converter::metaToObject) + override suspend fun read(device: D): T? = device.getProperty(propertyName)?.let(converter::metaToObject) override suspend fun write(device: D, value: T): Unit = device.writeProperty(propertyName, converter.objectToMeta(value)) diff --git a/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/MksPdr900Device.kt b/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/MksPdr900Device.kt index 7ba1e03..304102e 100644 --- a/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/MksPdr900Device.kt +++ b/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/MksPdr900Device.kt @@ -14,8 +14,12 @@ import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.transformations.MetaConverter + +//TODO this device is not tested class MksPdr900Device(context: Context, meta: Meta) : DeviceBySpec(MksPdr900Device, context, meta) { + private val address by meta.int(253) + private val portDelegate = lazy { val ports = context.request(Ports) ports.buildPort(meta["port"] ?: error("Port is not defined in device configuration")).synchronous() @@ -23,9 +27,6 @@ class MksPdr900Device(context: Context, meta: Meta) : DeviceBySpec(), Factory { - override fun build(context: Context, meta: Meta): MksPdr900Device = MksPdr900Device(context, meta) + const val DEFAULT_CHANNEL: Int = 5 + + override fun build(context: Context, meta: Meta): MksPdr900Device = MksPdr900Device(context, meta) val powerOn by booleanProperty(read = MksPdr900Device::readPowerOn, write = MksPdr900Device::writePowerOn) - val value by doubleProperty(read = MksPdr900Device::readChannelData) + val channel by logicalProperty(MetaConverter.int) + + val value by doubleProperty(read = { + readChannelData(channel.get() ?: DEFAULT_CHANNEL) + }) val error by logicalProperty(MetaConverter.string) diff --git a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt index 3b00e0c..d6c9482 100644 --- a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt +++ b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt @@ -194,9 +194,9 @@ class PiMotionMasterDevice( val disconnect by metaAction({ info = "Disconnect the program from the device if it is connected" }) { - if (port != null) { + port?.let{ stop() - port?.close() + it.close() } port = null updateLogical(connected, false)