Magix client refactoring
This commit is contained in:
parent
9c5b6db9d1
commit
aa58674a23
@ -2,8 +2,7 @@
|
|||||||
|
|
||||||
# DataForge-control
|
# DataForge-control
|
||||||
|
|
||||||
DataForge-control is a data acquisition framework (work in progress). It is
|
DataForge-control is a data acquisition framework (work in progress). It is based on DataForge, a software framework for automated data processing.
|
||||||
based on DataForge, a software framework for automated data processing.
|
|
||||||
This repository contains a prototype of API and simple implementation
|
This repository contains a prototype of API and simple implementation
|
||||||
of a slow control system, including a demo.
|
of a slow control system, including a demo.
|
||||||
|
|
||||||
@ -37,7 +36,7 @@ Generally, a Device has Properties that can be read and written. Also, some Acti
|
|||||||
can optionally be applied on a device (may or may not affect properties).
|
can optionally be applied on a device (may or may not affect properties).
|
||||||
|
|
||||||
- `base` - contains baseline `Device` implementation
|
- `base` - contains baseline `Device` implementation
|
||||||
[`DeviceBase`](dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/base/DeviceBase.kt)
|
[`DeviceBase`](dataforge-device-core/src/commonMain/kotlin/hep/dataforge/control/base/DeviceBase.kt)
|
||||||
and property implementation, including property asynchronous flows.
|
and property implementation, including property asynchronous flows.
|
||||||
|
|
||||||
- `controllers` - implements Message Controller that can be attached to the event bus, Message
|
- `controllers` - implements Message Controller that can be attached to the event bus, Message
|
||||||
|
@ -4,17 +4,21 @@ import hep.dataforge.control.api.respondMessage
|
|||||||
import hep.dataforge.control.controllers.DeviceManager
|
import hep.dataforge.control.controllers.DeviceManager
|
||||||
import hep.dataforge.control.controllers.DeviceMessage
|
import hep.dataforge.control.controllers.DeviceMessage
|
||||||
import hep.dataforge.meta.toJson
|
import hep.dataforge.meta.toJson
|
||||||
|
import hep.dataforge.meta.toMeta
|
||||||
|
import hep.dataforge.meta.wrap
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.request.post
|
import io.ktor.client.request.post
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
import io.ktor.http.Url
|
import io.ktor.http.Url
|
||||||
import io.ktor.http.contentType
|
import io.ktor.http.contentType
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.json
|
import kotlinx.serialization.json.json
|
||||||
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
@ -28,57 +32,70 @@ import kotlinx.serialization.json.json
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
|
||||||
* A stateful unique id generator
|
|
||||||
*/
|
|
||||||
interface IdGenerator{
|
|
||||||
operator fun invoke(message: DeviceMessage): String
|
|
||||||
}
|
|
||||||
|
|
||||||
object MagixClient {
|
/**
|
||||||
/**
|
* Communicate with server in [Magix format](https://github.com/waltz-controls/rfc/tree/master/1)
|
||||||
* Convert a [DeviceMessage] to [Waltz format](https://github.com/waltz-controls/rfc/tree/master/1)
|
*/
|
||||||
*/
|
class MagixClient(
|
||||||
fun DeviceMessage.toWaltz(id: String, parentId: String? = null): JsonObject = json {
|
val manager: DeviceManager,
|
||||||
"id" to id
|
val postUrl: Url,
|
||||||
if (parentId != null) {
|
val inbox: Flow<JsonObject>
|
||||||
"parentId" to parentId
|
): CoroutineScope {
|
||||||
}
|
|
||||||
"target" to "magix"
|
override val coroutineContext: CoroutineContext = manager.context.coroutineContext + Job(manager.context.coroutineContext[Job])
|
||||||
"origin" to "df"
|
|
||||||
"payload" to config.toJson()
|
private val client = HttpClient()
|
||||||
|
|
||||||
|
protected fun generateId(message: DeviceMessage, requestId: String?): String = if(requestId != null){
|
||||||
|
"$requestId.response"
|
||||||
|
} else{
|
||||||
|
"df[${message.hashCode()}"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun buildCallback(url: Url, idGenerator: IdGenerator): suspend (DeviceMessage) -> Unit {
|
private fun send(json: JsonObject) {
|
||||||
val client = HttpClient()
|
launch {
|
||||||
return { message ->
|
client.post<Unit>(postUrl) {
|
||||||
client.post(url) {
|
|
||||||
val messageId = idGenerator(message)
|
|
||||||
val waltzMessage = message.toWaltz(messageId)
|
|
||||||
this.contentType(ContentType.Application.Json)
|
this.contentType(ContentType.Application.Json)
|
||||||
body = waltzMessage.toString()
|
body = json.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun wrapMessage(message: DeviceMessage, requestId: String? = null): JsonObject = json {
|
||||||
|
"id" to generateId(message, requestId)
|
||||||
|
if (requestId != null) {
|
||||||
|
"parentId" to requestId
|
||||||
|
}
|
||||||
|
"target" to "magix"
|
||||||
|
"origin" to "df"
|
||||||
|
"payload" to message.config.toJson()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private val listenJob = launch {
|
||||||
|
manager.controller.messageOutput().collect { message ->
|
||||||
|
val json = wrapMessage(message)
|
||||||
|
send(json)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val respondJob = launch {
|
||||||
|
inbox.collect { json ->
|
||||||
|
val requestId = json["id"]?.primitive?.content
|
||||||
|
val payload = json["payload"]?.jsonObject
|
||||||
|
//TODO analyze action
|
||||||
|
|
||||||
|
if(payload != null){
|
||||||
|
val meta = payload.toMeta()
|
||||||
|
val request = DeviceMessage.wrap(meta)
|
||||||
|
val response = manager.respondMessage(request)
|
||||||
|
send(wrapMessage(response,requestId))
|
||||||
|
} else {
|
||||||
|
TODO("process heartbeat and other system messages")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Event loop for magix input and output flows
|
|
||||||
*/
|
|
||||||
fun DeviceManager.startMagix(
|
|
||||||
inbox: Flow<DeviceMessage>, // Inbox flow like SSE
|
|
||||||
outbox: suspend (DeviceMessage) -> Unit // outbox callback
|
|
||||||
): Job = context.launch {
|
|
||||||
launch {
|
|
||||||
controller.messageOutput().collect { message ->
|
|
||||||
outbox.invoke(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
launch {
|
|
||||||
inbox.collect { message ->
|
|
||||||
val response = respondMessage(message)
|
|
||||||
outbox.invoke(response)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user