API refactoring
This commit is contained in:
parent
3f8d62ebc4
commit
6500d5a05e
@ -1,5 +1,4 @@
|
||||
val dataforgeVersion by extra("0.1.9-dev")
|
||||
|
||||
val dataforgeVersion by extra("0.1.8")
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
|
@ -8,7 +8,7 @@ plugins {
|
||||
|
||||
val dataforgeVersion: String by rootProject.extra
|
||||
|
||||
useCoroutines(version = "1.3.7")
|
||||
useCoroutines()
|
||||
useSerialization()
|
||||
|
||||
kotlin {
|
||||
@ -28,6 +28,5 @@ kotlin {
|
||||
dependencies{
|
||||
}
|
||||
}
|
||||
val nativeMain by getting{}
|
||||
}
|
||||
}
|
@ -1,30 +1,26 @@
|
||||
package hep.dataforge.control.api
|
||||
|
||||
import hep.dataforge.control.api.Device.Companion.ACTION_LIST_ACTION
|
||||
import hep.dataforge.control.api.Device.Companion.DEVICE_TARGET
|
||||
import hep.dataforge.control.api.Device.Companion.EXECUTE_ACTION
|
||||
import hep.dataforge.control.api.Device.Companion.GET_PROPERTY_ACTION
|
||||
import hep.dataforge.control.api.Device.Companion.PROPERTY_LIST_ACTION
|
||||
import hep.dataforge.control.api.Device.Companion.SET_PROPERTY_ACTION
|
||||
import hep.dataforge.control.controllers.DeviceMessage
|
||||
import hep.dataforge.control.controllers.MessageData
|
||||
import hep.dataforge.control.controllers.wrap
|
||||
import hep.dataforge.io.Envelope
|
||||
import hep.dataforge.io.Responder
|
||||
import hep.dataforge.io.SimpleEnvelope
|
||||
import hep.dataforge.meta.Meta
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.meta.wrap
|
||||
import hep.dataforge.io.EnvelopeBuilder
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.provider.Type
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.io.Binary
|
||||
import kotlinx.io.Closeable
|
||||
|
||||
interface Consumer {
|
||||
fun consume(message: Envelope): Unit
|
||||
}
|
||||
|
||||
/**
|
||||
* General interface describing a managed Device
|
||||
*/
|
||||
@Type(DEVICE_TARGET)
|
||||
interface Device: Closeable, Responder {
|
||||
interface Device: Closeable{
|
||||
/**
|
||||
* List of supported property descriptors
|
||||
*/
|
||||
@ -74,13 +70,12 @@ interface Device: Closeable, Responder {
|
||||
* Send an action request and suspend caller while request is being processed.
|
||||
* Could return null if request does not return a meaningful answer.
|
||||
*/
|
||||
suspend fun exec(action: String, argument: MetaItem<*>? = null): MetaItem<*>?
|
||||
suspend fun execute(action: String, argument: MetaItem<*>? = null): MetaItem<*>?
|
||||
|
||||
override suspend fun respond(request: Envelope): Envelope {
|
||||
val requestMessage = DeviceMessage.wrap(request.meta)
|
||||
val responseMessage = respondMessage(requestMessage)
|
||||
return SimpleEnvelope(responseMessage.toMeta(), Binary.EMPTY)
|
||||
}
|
||||
/**
|
||||
*
|
||||
*/
|
||||
suspend fun respondWithData(request: Envelope): EnvelopeBuilder = error("Respond with data not implemented")
|
||||
|
||||
override fun close() {
|
||||
scope.cancel("The device is closed")
|
||||
@ -93,70 +88,102 @@ interface Device: Closeable, Responder {
|
||||
const val EXECUTE_ACTION = "execute"
|
||||
const val PROPERTY_LIST_ACTION = "propertyList"
|
||||
const val ACTION_LIST_ACTION = "actionList"
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun Device.respondMessage(
|
||||
request: DeviceMessage
|
||||
): DeviceMessage {
|
||||
val result: List<MessageData> = when (val action = request.type) {
|
||||
GET_PROPERTY_ACTION -> {
|
||||
request.data.map { property ->
|
||||
MessageData {
|
||||
name = property.name
|
||||
value = getProperty(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
SET_PROPERTY_ACTION -> {
|
||||
request.data.map { property ->
|
||||
val propertyName: String = property.name
|
||||
val propertyValue = property.value
|
||||
if (propertyValue == null) {
|
||||
invalidateProperty(propertyName)
|
||||
internal suspend fun respond(device: Device, deviceTarget: String, request: Envelope): Envelope {
|
||||
val target = request.meta["target"].string
|
||||
return try {
|
||||
if (request.data == null) {
|
||||
respondMessage(device, deviceTarget, DeviceMessage.wrap(request.meta)).wrap()
|
||||
} else if (target != null && target != deviceTarget) {
|
||||
error("Wrong target name $deviceTarget expected but $target found")
|
||||
} else {
|
||||
setProperty(propertyName, propertyValue)
|
||||
}
|
||||
MessageData {
|
||||
name = propertyName
|
||||
value = getProperty(propertyName)
|
||||
}
|
||||
}
|
||||
}
|
||||
EXECUTE_ACTION -> {
|
||||
request.data.map { payload ->
|
||||
MessageData {
|
||||
name = payload.name
|
||||
value = exec(payload.name, payload.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
PROPERTY_LIST_ACTION -> {
|
||||
propertyDescriptors.map { descriptor ->
|
||||
MessageData {
|
||||
name = descriptor.name
|
||||
value = MetaItem.NodeItem(descriptor.config)
|
||||
val response = device.respondWithData(request).apply {
|
||||
meta {
|
||||
"target" put request.meta["source"].string
|
||||
"source" put deviceTarget
|
||||
}
|
||||
}
|
||||
return response.build()
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
DeviceMessage.fail {
|
||||
comment = ex.message
|
||||
}.wrap()
|
||||
}
|
||||
}
|
||||
|
||||
ACTION_LIST_ACTION -> {
|
||||
actionDescriptors.map { descriptor ->
|
||||
MessageData {
|
||||
name = descriptor.name
|
||||
value = MetaItem.NodeItem(descriptor.config)
|
||||
internal suspend fun respondMessage(
|
||||
device: Device,
|
||||
deviceTarget: String,
|
||||
request: DeviceMessage
|
||||
): DeviceMessage {
|
||||
return try {
|
||||
val result: List<MessageData> = when (val action = request.type) {
|
||||
GET_PROPERTY_ACTION -> {
|
||||
request.data.map { property ->
|
||||
MessageData {
|
||||
name = property.name
|
||||
value = device.getProperty(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
SET_PROPERTY_ACTION -> {
|
||||
request.data.map { property ->
|
||||
val propertyName: String = property.name
|
||||
val propertyValue = property.value
|
||||
if (propertyValue == null) {
|
||||
device.invalidateProperty(propertyName)
|
||||
} else {
|
||||
device.setProperty(propertyName, propertyValue)
|
||||
}
|
||||
MessageData {
|
||||
name = propertyName
|
||||
value = device.getProperty(propertyName)
|
||||
}
|
||||
}
|
||||
}
|
||||
EXECUTE_ACTION -> {
|
||||
request.data.map { payload ->
|
||||
MessageData {
|
||||
name = payload.name
|
||||
value = device.execute(payload.name, payload.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
PROPERTY_LIST_ACTION -> {
|
||||
device.propertyDescriptors.map { descriptor ->
|
||||
MessageData {
|
||||
name = descriptor.name
|
||||
value = MetaItem.NodeItem(descriptor.config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ACTION_LIST_ACTION -> {
|
||||
device.actionDescriptors.map { descriptor ->
|
||||
MessageData {
|
||||
name = descriptor.name
|
||||
value = MetaItem.NodeItem(descriptor.config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
error("Unrecognized action $action")
|
||||
}
|
||||
}
|
||||
DeviceMessage.ok {
|
||||
target = request.source
|
||||
source = deviceTarget
|
||||
data = result
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
DeviceMessage.fail {
|
||||
comment = ex.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
error("Unrecognized action $action")
|
||||
}
|
||||
}
|
||||
return DeviceMessage.ok {
|
||||
target = request.source
|
||||
data = result
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun Device.exec(name: String, meta: Meta?) = exec(name, meta?.let { MetaItem.NodeItem(it) })
|
||||
suspend fun Device.execute(name: String, meta: Meta?): MetaItem<*>? = execute(name, meta?.let { MetaItem.NodeItem(it) })
|
@ -13,7 +13,7 @@ import hep.dataforge.provider.Provider
|
||||
* A hub that could locate multiple devices and redirect actions to them
|
||||
*/
|
||||
interface DeviceHub : Provider {
|
||||
val devices: Map<Name, Device>
|
||||
val devices: Map<Name, Device>//TODO use token instead of Names
|
||||
|
||||
override val defaultTarget: String get() = Device.DEVICE_TARGET
|
||||
|
||||
@ -44,18 +44,24 @@ suspend fun DeviceHub.setProperty(deviceName: Name, propertyName: String, value:
|
||||
this[deviceName].setProperty(propertyName, value)
|
||||
}
|
||||
|
||||
suspend fun DeviceHub.exec(deviceName: Name, command: String, argument: MetaItem<*>?): MetaItem<*>? =
|
||||
this[deviceName].exec(command, argument)
|
||||
suspend fun DeviceHub.execute(deviceName: Name, command: String, argument: MetaItem<*>?): MetaItem<*>? =
|
||||
this[deviceName].execute(command, argument)
|
||||
|
||||
suspend fun DeviceHub.respondMessage(request: DeviceMessage): DeviceMessage {
|
||||
val device = this[request.target?.toName() ?: Name.EMPTY]
|
||||
|
||||
return device.respondMessage(request)
|
||||
return try {
|
||||
val targetName = request.target?.toName() ?: Name.EMPTY
|
||||
val device = this[targetName]
|
||||
Device.respondMessage(device, targetName.toString(), request)
|
||||
} catch (ex: Exception) {
|
||||
DeviceMessage.fail {
|
||||
comment = ex.message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun DeviceHub.respond(request: Envelope): Envelope {
|
||||
val target = request.meta[DeviceMessage.TARGET_KEY].string
|
||||
val device = this[target?.toName() ?: Name.EMPTY]
|
||||
val target = request.meta[DeviceMessage.TARGET_KEY].string ?: defaultTarget
|
||||
val device = this[target.toName()]
|
||||
|
||||
return device.respond(request)
|
||||
return Device.respond(device, target, request)
|
||||
}
|
@ -8,5 +8,7 @@ import hep.dataforge.meta.MetaItem
|
||||
*/
|
||||
interface DeviceListener {
|
||||
fun propertyChanged(propertyName: String, value: MetaItem<*>?)
|
||||
fun actionExecuted(action:String, argument: MetaItem<*>?, result: MetaItem<*>?)
|
||||
|
||||
//TODO add general message listener method
|
||||
}
|
@ -54,7 +54,7 @@ abstract class DeviceBase : Device {
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun exec(action: String, argument: MetaItem<*>?): MetaItem<*>? =
|
||||
override suspend fun execute(action: String, argument: MetaItem<*>?): MetaItem<*>? =
|
||||
(actions[action] ?: error("Request with name $action not defined")).invoke(argument)
|
||||
|
||||
|
||||
|
@ -1,14 +1,13 @@
|
||||
package hep.dataforge.control.controllers
|
||||
|
||||
import hep.dataforge.control.api.Consumer
|
||||
import hep.dataforge.control.api.Device
|
||||
import hep.dataforge.control.api.DeviceListener
|
||||
import hep.dataforge.control.api.respondMessage
|
||||
import hep.dataforge.control.controllers.DeviceMessage.Companion.PROPERTY_CHANGED_ACTION
|
||||
import hep.dataforge.io.Consumer
|
||||
import hep.dataforge.io.Envelope
|
||||
import hep.dataforge.io.Responder
|
||||
import hep.dataforge.io.SimpleEnvelope
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.consumeAsFlow
|
||||
@ -19,7 +18,7 @@ class DeviceController(
|
||||
val device: Device,
|
||||
val deviceTarget: String,
|
||||
val scope: CoroutineScope = device.scope
|
||||
) : Consumer, Responder, DeviceListener {
|
||||
) : Responder, Consumer, DeviceListener {
|
||||
|
||||
init {
|
||||
device.registerListener(this, this)
|
||||
@ -27,45 +26,12 @@ class DeviceController(
|
||||
|
||||
private val outputChannel = Channel<Envelope>(Channel.CONFLATED)
|
||||
|
||||
override fun consume(message: Envelope) {
|
||||
// Fire the respond procedure and forget about the result
|
||||
scope.launch {
|
||||
respond(message)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun respondMessage(message: DeviceMessage): DeviceMessage {
|
||||
return try {
|
||||
device.respondMessage(message).apply {
|
||||
target = message.source
|
||||
source = deviceTarget
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
DeviceMessage.fail {
|
||||
comment = ex.message
|
||||
}
|
||||
}
|
||||
return Device.respondMessage(device, deviceTarget, message)
|
||||
}
|
||||
|
||||
override suspend fun respond(request: Envelope): Envelope {
|
||||
val target = request.meta["target"].string
|
||||
return try {
|
||||
if (request.data == null) {
|
||||
respondMessage(DeviceMessage.wrap(request.meta)).wrap()
|
||||
}else if(target != null && target != deviceTarget) {
|
||||
error("Wrong target name $deviceTarget expected but ${target} found")
|
||||
} else {
|
||||
val response = device.respond(request)
|
||||
return SimpleEnvelope(response.meta.edit {
|
||||
"target" put request.meta["source"].string
|
||||
"source" put deviceTarget
|
||||
}, response.data)
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
DeviceMessage.fail {
|
||||
comment = ex.message
|
||||
}.wrap()
|
||||
}
|
||||
return Device.respond(device, deviceTarget, request)
|
||||
}
|
||||
|
||||
override fun propertyChanged(propertyName: String, value: MetaItem<*>?) {
|
||||
@ -87,8 +53,14 @@ class DeviceController(
|
||||
|
||||
fun output() = outputChannel.consumeAsFlow()
|
||||
|
||||
override fun consume(message: Envelope) {
|
||||
// Fire the respond procedure and forget about the result
|
||||
scope.launch {
|
||||
respond(message)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,12 @@
|
||||
package hep.dataforge.control.controllers
|
||||
|
||||
import hep.dataforge.control.api.DeviceHub
|
||||
import hep.dataforge.control.api.DeviceListener
|
||||
import hep.dataforge.control.api.get
|
||||
import hep.dataforge.control.api.respondMessage
|
||||
import hep.dataforge.io.Consumer
|
||||
import hep.dataforge.control.api.*
|
||||
import hep.dataforge.io.Envelope
|
||||
import hep.dataforge.io.Responder
|
||||
import hep.dataforge.io.SimpleEnvelope
|
||||
import hep.dataforge.meta.*
|
||||
import hep.dataforge.meta.MetaItem
|
||||
import hep.dataforge.meta.get
|
||||
import hep.dataforge.meta.string
|
||||
import hep.dataforge.meta.wrap
|
||||
import hep.dataforge.names.Name
|
||||
import hep.dataforge.names.toName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@ -59,39 +57,28 @@ class HubController(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun respondMessage(message: DeviceMessage): DeviceMessage {
|
||||
return try {
|
||||
val targetName = message.target?.toName() ?: Name.EMPTY
|
||||
val device = hub[targetName]
|
||||
device.respondMessage(message).apply {
|
||||
target = message.source
|
||||
source = targetName.toString()
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
DeviceMessage.fail {
|
||||
comment = ex.message
|
||||
}
|
||||
suspend fun respondMessage(message: DeviceMessage): DeviceMessage = try {
|
||||
val targetName = message.target?.toName() ?: Name.EMPTY
|
||||
val device = hub[targetName]
|
||||
Device.respondMessage(device, targetName.toString(), message)
|
||||
} catch (ex: Exception) {
|
||||
DeviceMessage.fail {
|
||||
comment = ex.message
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun respond(request: Envelope): Envelope {
|
||||
override suspend fun respond(request: Envelope): Envelope = try {
|
||||
val targetName = request.meta[DeviceMessage.TARGET_KEY].string?.toName() ?: Name.EMPTY
|
||||
return try {
|
||||
val device = hub[targetName]
|
||||
if (request.data == null) {
|
||||
respondMessage(DeviceMessage.wrap(request.meta)).wrap()
|
||||
} else {
|
||||
val response = device.respond(request)
|
||||
return SimpleEnvelope(response.meta.edit {
|
||||
DeviceMessage.TARGET_KEY put request.meta[DeviceMessage.SOURCE_KEY].string
|
||||
DeviceMessage.SOURCE_KEY put targetName.toString()
|
||||
}, response.data)
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
DeviceMessage.fail {
|
||||
comment = ex.message
|
||||
}.wrap()
|
||||
val device = hub[targetName]
|
||||
if (request.data == null) {
|
||||
Device.respondMessage(device, targetName.toString(), DeviceMessage.wrap(request.meta)).wrap()
|
||||
} else {
|
||||
Device.respond(device, targetName.toString(), request)
|
||||
}
|
||||
} catch (ex: Exception) {
|
||||
DeviceMessage.fail {
|
||||
comment = ex.message
|
||||
}.wrap()
|
||||
}
|
||||
|
||||
override fun consume(message: Envelope) {
|
||||
|
@ -185,8 +185,6 @@ fun Application.deviceModule(
|
||||
}
|
||||
|
||||
post("message") {
|
||||
val target: String by call.request.queryParameters
|
||||
val device = manager[target]
|
||||
val body = call.receiveText()
|
||||
val json = Json.parseJson(body) as? JsonObject
|
||||
?: throw IllegalArgumentException("The body is not a json object")
|
||||
@ -194,7 +192,7 @@ fun Application.deviceModule(
|
||||
|
||||
val request = DeviceMessage.wrap(meta)
|
||||
|
||||
val response = device.respondMessage(request)
|
||||
val response = manager.respondMessage(request)
|
||||
call.respondMessage(response)
|
||||
}
|
||||
|
||||
@ -204,7 +202,6 @@ fun Application.deviceModule(
|
||||
route("{property}") {
|
||||
get("get") {
|
||||
val target: String by call.parameters
|
||||
val device = manager[target]
|
||||
val property: String by call.parameters
|
||||
val request = DeviceMessage {
|
||||
type = GET_PROPERTY_ACTION
|
||||
@ -215,13 +212,11 @@ fun Application.deviceModule(
|
||||
}
|
||||
}
|
||||
|
||||
val response = device.respondMessage(request)
|
||||
val response = manager.respondMessage(request)
|
||||
call.respondMessage(response)
|
||||
}
|
||||
post("set") {
|
||||
val target: String by call.parameters
|
||||
val device = manager[target]
|
||||
|
||||
val property: String by call.parameters
|
||||
val body = call.receiveText()
|
||||
val json = Json.parseJson(body)
|
||||
@ -236,7 +231,7 @@ fun Application.deviceModule(
|
||||
}
|
||||
}
|
||||
|
||||
val response = device.respondMessage(request)
|
||||
val response = manager.respondMessage(request)
|
||||
call.respondMessage(response)
|
||||
}
|
||||
}
|
||||
|
11
motors/build.gradle.kts
Normal file
11
motors/build.gradle.kts
Normal file
@ -0,0 +1,11 @@
|
||||
plugins {
|
||||
id("scientifik.jvm")
|
||||
id("scientifik.publish")
|
||||
}
|
||||
|
||||
//TODO to be moved to a separate project
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation(project(":dataforge-device-core"))
|
||||
}
|
@ -38,7 +38,8 @@ include(
|
||||
":dataforge-device-serial",
|
||||
":dataforge-device-server",
|
||||
":dataforge-device-client",
|
||||
":demo"
|
||||
":demo",
|
||||
":motors"
|
||||
)
|
||||
|
||||
//includeBuild("../dataforge-core")
|
||||
|
Loading…
Reference in New Issue
Block a user