Dev #8

Merged
altavir merged 78 commits from dev into master 2023-03-04 16:47:55 +03:00
18 changed files with 113 additions and 53 deletions
Showing only changes of commit 025a444db8 - Show all commits

View File

@ -1,6 +1,6 @@
plugins { plugins {
kotlin("jvm") kotlin("jvm")
id("org.openjfx.javafxplugin") id("org.openjfx.javafxplugin") version "0.0.10"
application application
} }
@ -19,9 +19,9 @@ dependencies {
implementation(projects.magix.magixServer) implementation(projects.magix.magixServer)
implementation(projects.magix.magixRsocket) implementation(projects.magix.magixRsocket)
implementation(projects.controlsMagixClient) implementation(projects.controlsMagixClient)
implementation(projects.controlsXodus) implementation(projects.controlsStorage.controlsXodus)
implementation(projects.controlsMongo) implementation(projects.magix.magixStorage.magixStorageXodus)
implementation(projects.controlsStorage) // implementation(projects.controlsMongo)
implementation("io.ktor:ktor-client-cio:$ktorVersion") implementation("io.ktor:ktor-client-cio:$ktorVersion")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.3.1") 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-entity-store:1.3.232")
implementation("org.jetbrains.xodus:xodus-environment: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-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<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach { tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {

View File

@ -1,10 +1,10 @@
package ru.mipt.npm.controls.demo.car package ru.mipt.npm.controls.demo.car
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.mipt.npm.controls.api.DeviceMessage
import ru.mipt.npm.controls.api.PropertyChangedMessage 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.MagixEndpoint
import ru.mipt.npm.magix.api.subscribe
import ru.mipt.npm.magix.rsocket.rSocketWithWebSockets import ru.mipt.npm.magix.rsocket.rSocketWithWebSockets
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.Factory import space.kscience.dataforge.context.Factory
@ -16,31 +16,29 @@ 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<DeviceMessage>.startMagixVirtualCarUpdate() { private fun MagixEndpoint.launchMagixVirtualCarUpdate() = launch {
launch { subscribe(controlsMagixFormat).collect { (_, payload) ->
subscribe().collect { magix -> (payload as? PropertyChangedMessage)?.let { message ->
(magix.payload as? PropertyChangedMessage)?.let { message -> if (message.sourceDevice == Name.parse("virtual-car")) {
if (message.sourceDevice == Name.parse("virtual-car")) { when (message.property) {
when (message.property) { "acceleration" -> IVirtualCar.acceleration.write(Vector2D.metaToObject(message.value))
"acceleration" -> IVirtualCar.acceleration.write(Vector2D.metaToObject(message.value))
}
} }
} }
} }
} }
} }
@OptIn(ExperimentalTime::class) @OptIn(ExperimentalTime::class)
override suspend fun open() { override suspend fun open() {
super.open() super.open()
val magixEndpoint = MagixEndpoint.rSocketWithWebSockets( val magixEndpoint = MagixEndpoint.rSocketWithWebSockets(
meta["magixServerHost"].string ?: "localhost", meta["magixServerHost"].string ?: "localhost",
DeviceMessage.serializer()
) )
launch { launch {
magixEndpoint.startMagixVirtualCarUpdate() magixEndpoint.launchMagixVirtualCarUpdate()
} }
} }

View File

@ -8,36 +8,29 @@ import javafx.scene.layout.Priority
import javafx.stage.Stage import javafx.stage.Stage
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import ru.mipt.npm.controls.api.DeviceMessage
import ru.mipt.npm.controls.client.connectToMagix 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.demo.car.IVirtualCar.Companion.acceleration
import ru.mipt.npm.controls.mongo.DefaultAsynchronousMongoClientFactory import ru.mipt.npm.controls.manager.DeviceManager
import ru.mipt.npm.controls.storage.store import ru.mipt.npm.controls.manager.install
import ru.mipt.npm.controls.storage.storeMessages import ru.mipt.npm.controls.storage.storeMessages
import ru.mipt.npm.controls.xodus.XODUS_STORE_PROPERTY import ru.mipt.npm.controls.xodus.XodusDeviceMessageStorage
import ru.mipt.npm.controls.xodus.XodusEventStorage
import ru.mipt.npm.magix.api.MagixEndpoint import ru.mipt.npm.magix.api.MagixEndpoint
import ru.mipt.npm.magix.rsocket.rSocketWithTcp import ru.mipt.npm.magix.rsocket.rSocketWithTcp
import ru.mipt.npm.magix.server.startMagixServer import ru.mipt.npm.magix.server.startMagixServer
import ru.mipt.npm.magix.storage.xodus.storeInXodus
import space.kscience.dataforge.context.* import space.kscience.dataforge.context.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import tornadofx.* import tornadofx.*
import java.nio.file.Paths import java.nio.file.Paths
internal object VirtualCarControllerConfig {
val deviceEntityStorePath = Paths.get(".messages")
val magixEntityStorePath = Paths.get(".server_messages")
}
class VirtualCarController : Controller(), ContextAware { class VirtualCarController : Controller(), ContextAware {
var virtualCar: VirtualCar? = null var virtualCar: VirtualCar? = null
var magixVirtualCar: MagixVirtualCar? = null var magixVirtualCar: MagixVirtualCar? = null
var magixServer: ApplicationEngine? = null var magixServer: ApplicationEngine? = null
var xodusStorageJob: Job? = null var xodusStorageJob: Job? = null
var mongoStorageJob: Job? = null var storageEndpoint: MagixEndpoint? = null
//var mongoStorageJob: Job? = null
override val context = Context("demoDevice") { override val context = Context("demoDevice") {
plugin(DeviceManager) plugin(DeviceManager)
@ -45,7 +38,7 @@ class VirtualCarController : Controller(), ContextAware {
private val deviceManager = context.fetch(DeviceManager, Meta { private val deviceManager = context.fetch(DeviceManager, Meta {
"xodusConfig" put { "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) virtualCar = deviceManager.install("virtual-car", VirtualCar)
//starting magix event loop and connect it to entity store //starting magix event loop and connect it to entity store
magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true) { flow -> magixServer = startMagixServer(enableRawRSocket = true, enableZmq = true)
store(flow, XodusEventStorage, Meta {
XODUS_STORE_PROPERTY put VirtualCarControllerConfig.magixEntityStorePath.toString() storageEndpoint = MagixEndpoint.rSocketWithTcp("localhost").apply {
}) storeInXodus(this@launch, magixEntityStorePath)
store(flow, DefaultAsynchronousMongoClientFactory)
} }
magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar) magixVirtualCar = deviceManager.install("magix-virtual-car", MagixVirtualCar)
//connect to device entity store //connect to device entity store
xodusStorageJob = deviceManager.storeMessages(XodusEventStorage) xodusStorageJob = deviceManager.storeMessages(XodusDeviceMessageStorage)
//Create mongo client and connect to MongoDB //Create mongo client and connect to MongoDB
mongoStorageJob = deviceManager.storeMessages(DefaultAsynchronousMongoClientFactory) //mongoStorageJob = deviceManager.storeMessages(DefaultAsynchronousMongoClientFactory)
//Launch device client and connect it to the server //Launch device client and connect it to the server
val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost", DeviceMessage.serializer()) val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost")
deviceManager.connectToMagix(deviceEndpoint) deviceManager.connectToMagix(deviceEndpoint)
} }
} }
@ -81,6 +74,11 @@ class VirtualCarController : Controller(), ContextAware {
logger.info { "Virtual car server stopped" } logger.info { "Virtual car server stopped" }
context.close() 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 { action {
controller.virtualCar?.run { controller.virtualCar?.run {
launch { launch {
acceleration.write(Vector2D(accelerationXProperty.get(), acceleration.write(
accelerationYProperty.get())) Vector2D(
accelerationXProperty.get(),
accelerationYProperty.get()
)
)
} }
} }
} }

View File

@ -34,9 +34,9 @@ public suspend fun <T> MagixEndpoint.broadcast(
origin: String = format.defaultFormat, origin: String = format.defaultFormat,
) { ) {
val message = MagixMessage( val message = MagixMessage(
origin = origin,
payload = magixJson.encodeToJsonElement(format.serializer, payload),
format = format.defaultFormat, format = format.defaultFormat,
payload = magixJson.encodeToJsonElement(format.serializer, payload),
origin = origin,
target = target, target = target,
id = id, id = id,
parentId = parentId, parentId = parentId,

View File

@ -25,9 +25,9 @@ import kotlinx.serialization.json.JsonElement
*/ */
@Serializable @Serializable
public data class MagixMessage( public data class MagixMessage(
val origin: String, val format: String,
val payload: JsonElement, val payload: JsonElement,
val format: String = origin, val origin: String,
val target: String? = null, val target: String? = null,
val id: String? = null, val id: String? = null,
val parentId: String? = null, val parentId: String? = null,

View File

@ -18,9 +18,9 @@ public fun <T, R> CoroutineScope.launchMagixConverter(
): Job = endpoint.subscribe(filter).onEach { message-> ): Job = endpoint.subscribe(filter).onEach { message->
val newPayload = transformer(message.payload) val newPayload = transformer(message.payload)
val transformed: MagixMessage = MagixMessage( val transformed: MagixMessage = MagixMessage(
newOrigin ?: message.origin,
newPayload,
outputFormat, outputFormat,
newPayload,
newOrigin ?: message.origin,
message.target, message.target,
message.id, message.id,
message.parentId, message.parentId,

View File

@ -23,7 +23,7 @@ suspend fun MagixEndpoint.sendJson(
parentId: String? = null, parentId: String? = null,
user: JsonElement? = null, user: JsonElement? = null,
builder: JsonObjectBuilder.() -> Unit 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 internal const val numberOfMessages = 100

View File

@ -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 * @param applicationConfiguration optional additional configuration for magix loop server
*/ */
public fun CoroutineScope.startMagixServer( public fun CoroutineScope.startMagixServer(

View File

@ -1,13 +1,18 @@
package ru.mipt.npm.magix.storage.xodus package ru.mipt.npm.magix.storage.xodus
import jetbrains.exodus.entitystore.Entity
import jetbrains.exodus.entitystore.PersistentEntityStore import jetbrains.exodus.entitystore.PersistentEntityStore
import jetbrains.exodus.entitystore.PersistentEntityStores
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.serialization.encodeToString import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.JsonObject
import ru.mipt.npm.magix.api.MagixEndpoint 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.MagixMessage
import ru.mipt.npm.magix.api.MagixMessageFilter import ru.mipt.npm.magix.api.MagixMessageFilter
import java.nio.file.Path
public class XodusMagixStorage( public class XodusMagixStorage(
scope: CoroutineScope, scope: CoroutineScope,
@ -17,7 +22,7 @@ public class XodusMagixStorage(
) : AutoCloseable { ) : AutoCloseable {
//TODO consider message buffering //TODO consider message buffering
private val subscriptionJob = endpoint.subscribe(filter).onEach { message -> internal val subscriptionJob = endpoint.subscribe(filter).onEach { message ->
store.executeInTransaction { transaction -> store.executeInTransaction { transaction ->
transaction.newEntity(MAGIC_MESSAGE_ENTITY_TYPE).apply { transaction.newEntity(MAGIC_MESSAGE_ENTITY_TYPE).apply {
setProperty(MagixMessage::origin.name, message.origin) setProperty(MagixMessage::origin.name, message.origin)
@ -41,6 +46,43 @@ public class XodusMagixStorage(
} }
}.launchIn(scope) }.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<MagixMessage>) -> 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<MagixMessage>) -> Unit,
): Unit = store.executeInReadonlyTransaction { transaction ->
val sequence = transaction.getAll(MAGIC_MESSAGE_ENTITY_TYPE).asSequence().map { entity ->
entity.parseMagixMessage()
}
block(sequence)
}
override fun close() { override fun close() {
subscriptionJob.cancel() subscriptionJob.cancel()
} }
@ -49,3 +91,21 @@ public class XodusMagixStorage(
public const val MAGIC_MESSAGE_ENTITY_TYPE: String = "magix.message" 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)
}

View File

@ -44,9 +44,9 @@ include(
":controls-serial", ":controls-serial",
":controls-server", ":controls-server",
":controls-opcua", ":controls-opcua",
":controls-xodus",
// ":controls-mongo", // ":controls-mongo",
":controls-storage", ":controls-storage",
":controls-storage:controls-xodus",
":magix", ":magix",
":magix:magix-api", ":magix:magix-api",
":magix:magix-server", ":magix:magix-server",
@ -58,6 +58,6 @@ include(
":magix:magix-storage:magix-storage-xodus", ":magix:magix-storage:magix-storage-xodus",
":controls-magix-client", ":controls-magix-client",
":motors", ":motors",
":demo", ":demo:all-things",
// ":demo:car", ":demo:car",
) )