Another API refactoring
This commit is contained in:
parent
6500d5a05e
commit
20079e62da
@ -1,8 +1,8 @@
|
|||||||
package hep.dataforge.control.client
|
package hep.dataforge.control.client
|
||||||
|
|
||||||
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.control.controllers.respondMessage
|
||||||
import hep.dataforge.meta.toJson
|
import hep.dataforge.meta.toJson
|
||||||
import hep.dataforge.meta.toMeta
|
import hep.dataforge.meta.toMeta
|
||||||
import hep.dataforge.meta.wrap
|
import hep.dataforge.meta.wrap
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
package hep.dataforge.control.api
|
package hep.dataforge.control.api
|
||||||
|
|
||||||
import hep.dataforge.control.api.Device.Companion.DEVICE_TARGET
|
import hep.dataforge.control.api.Device.Companion.DEVICE_TARGET
|
||||||
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.Envelope
|
||||||
import hep.dataforge.io.EnvelopeBuilder
|
import hep.dataforge.io.EnvelopeBuilder
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.MetaItem
|
||||||
import hep.dataforge.provider.Type
|
import hep.dataforge.provider.Type
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
@ -20,7 +18,7 @@ interface Consumer {
|
|||||||
* General interface describing a managed Device
|
* General interface describing a managed Device
|
||||||
*/
|
*/
|
||||||
@Type(DEVICE_TARGET)
|
@Type(DEVICE_TARGET)
|
||||||
interface Device: Closeable{
|
interface Device : Closeable {
|
||||||
/**
|
/**
|
||||||
* List of supported property descriptors
|
* List of supported property descriptors
|
||||||
*/
|
*/
|
||||||
@ -70,119 +68,23 @@ interface Device: Closeable{
|
|||||||
* Send an action request and suspend caller while request is being processed.
|
* Send an action request and suspend caller while request is being processed.
|
||||||
* Could return null if request does not return a meaningful answer.
|
* Could return null if request does not return a meaningful answer.
|
||||||
*/
|
*/
|
||||||
suspend fun execute(action: String, argument: MetaItem<*>? = null): MetaItem<*>?
|
suspend fun execute(command: String, argument: MetaItem<*>? = null): MetaItem<*>?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
* A request with binary data or for binary response (or both). This request does not cover basic functionality like
|
||||||
|
* [setProperty], [getProperty] or [execute] and not defined for a generic device.
|
||||||
|
*
|
||||||
|
* TODO implement Responder after DF 0.1.9
|
||||||
*/
|
*/
|
||||||
suspend fun respondWithData(request: Envelope): EnvelopeBuilder = error("Respond with data not implemented")
|
suspend fun respond(request: Envelope): EnvelopeBuilder = error("No binary response defined")
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
scope.cancel("The device is closed")
|
scope.cancel("The device is closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object{
|
companion object {
|
||||||
const val DEVICE_TARGET = "device"
|
const val DEVICE_TARGET = "device"
|
||||||
const val GET_PROPERTY_ACTION = "read"
|
|
||||||
const val SET_PROPERTY_ACTION = "write"
|
|
||||||
const val EXECUTE_ACTION = "execute"
|
|
||||||
const val PROPERTY_LIST_ACTION = "propertyList"
|
|
||||||
const val ACTION_LIST_ACTION = "actionList"
|
|
||||||
|
|
||||||
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 {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,6 @@
|
|||||||
package hep.dataforge.control.api
|
package hep.dataforge.control.api
|
||||||
|
|
||||||
import hep.dataforge.control.controllers.DeviceMessage
|
|
||||||
import hep.dataforge.io.Envelope
|
|
||||||
import hep.dataforge.meta.MetaItem
|
import hep.dataforge.meta.MetaItem
|
||||||
import hep.dataforge.meta.get
|
|
||||||
import hep.dataforge.meta.string
|
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import hep.dataforge.provider.Provider
|
import hep.dataforge.provider.Provider
|
||||||
@ -47,21 +43,10 @@ suspend fun DeviceHub.setProperty(deviceName: Name, propertyName: String, value:
|
|||||||
suspend fun DeviceHub.execute(deviceName: Name, command: String, argument: MetaItem<*>?): MetaItem<*>? =
|
suspend fun DeviceHub.execute(deviceName: Name, command: String, argument: MetaItem<*>?): MetaItem<*>? =
|
||||||
this[deviceName].execute(command, argument)
|
this[deviceName].execute(command, argument)
|
||||||
|
|
||||||
suspend fun DeviceHub.respondMessage(request: DeviceMessage): DeviceMessage {
|
|
||||||
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 {
|
//suspend fun DeviceHub.respond(request: Envelope): EnvelopeBuilder {
|
||||||
val target = request.meta[DeviceMessage.TARGET_KEY].string ?: defaultTarget
|
// val target = request.meta[DeviceMessage.TARGET_KEY].string ?: defaultTarget
|
||||||
val device = this[target.toName()]
|
// val device = this[target.toName()]
|
||||||
|
//
|
||||||
return Device.respond(device, target, request)
|
// return device.respond(device, target, request)
|
||||||
}
|
//}
|
@ -8,7 +8,7 @@ import hep.dataforge.meta.MetaItem
|
|||||||
*/
|
*/
|
||||||
interface DeviceListener {
|
interface DeviceListener {
|
||||||
fun propertyChanged(propertyName: String, value: MetaItem<*>?)
|
fun propertyChanged(propertyName: String, value: MetaItem<*>?)
|
||||||
fun actionExecuted(action:String, argument: MetaItem<*>?, result: MetaItem<*>?)
|
fun actionExecuted(action: String, argument: MetaItem<*>?, result: MetaItem<*>?) {}
|
||||||
|
|
||||||
//TODO add general message listener method
|
//TODO add general message listener method
|
||||||
}
|
}
|
@ -13,50 +13,60 @@ interface Action {
|
|||||||
suspend operator fun invoke(arg: MetaItem<*>? = null): MetaItem<*>?
|
suspend operator fun invoke(arg: MetaItem<*>? = null): MetaItem<*>?
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimpleAction(
|
private fun DeviceBase.actionExecuted(action: String, argument: MetaItem<*>?, result: MetaItem<*>?){
|
||||||
|
notifyListeners { actionExecuted(action, argument, result) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A stand-alone action
|
||||||
|
*/
|
||||||
|
class IsolatedAction(
|
||||||
override val name: String,
|
override val name: String,
|
||||||
override val descriptor: ActionDescriptor,
|
override val descriptor: ActionDescriptor,
|
||||||
|
val callback: (action: String, argument: MetaItem<*>?, result: MetaItem<*>?) -> Unit,
|
||||||
val block: suspend (MetaItem<*>?) -> MetaItem<*>?
|
val block: suspend (MetaItem<*>?) -> MetaItem<*>?
|
||||||
) : Action {
|
) : Action {
|
||||||
override suspend fun invoke(arg: MetaItem<*>?): MetaItem<*>? = block(arg)
|
override suspend fun invoke(arg: MetaItem<*>?): MetaItem<*>? = block(arg).also {
|
||||||
|
callback(name, arg, it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ActionDelegate<D : DeviceBase>(
|
class ActionDelegate<D : DeviceBase>(
|
||||||
val owner: D,
|
val owner: D,
|
||||||
val descriptorBuilder: ActionDescriptor.()->Unit = {},
|
val descriptorBuilder: ActionDescriptor.() -> Unit = {},
|
||||||
val block: suspend (MetaItem<*>?) -> MetaItem<*>?
|
val block: suspend (MetaItem<*>?) -> MetaItem<*>?
|
||||||
) : ReadOnlyProperty<D, Action> {
|
) : ReadOnlyProperty<D, Action> {
|
||||||
override fun getValue(thisRef: D, property: KProperty<*>): Action {
|
override fun getValue(thisRef: D, property: KProperty<*>): Action {
|
||||||
val name = property.name
|
val name = property.name
|
||||||
return owner.registerAction(name) {
|
return owner.registerAction(name) {
|
||||||
SimpleAction(name, ActionDescriptor(name).apply(descriptorBuilder), block)
|
IsolatedAction(name, ActionDescriptor(name).apply(descriptorBuilder), owner::actionExecuted, block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <D : DeviceBase> D.request(
|
fun <D : DeviceBase> D.request(
|
||||||
descriptorBuilder: ActionDescriptor.()->Unit = {},
|
descriptorBuilder: ActionDescriptor.() -> Unit = {},
|
||||||
block: suspend (MetaItem<*>?) -> MetaItem<*>?
|
block: suspend (MetaItem<*>?) -> MetaItem<*>?
|
||||||
): ActionDelegate<D> = ActionDelegate(this, descriptorBuilder, block)
|
): ActionDelegate<D> = ActionDelegate(this, descriptorBuilder, block)
|
||||||
|
|
||||||
fun <D : DeviceBase> D.requestValue(
|
fun <D : DeviceBase> D.requestValue(
|
||||||
descriptorBuilder: ActionDescriptor.()->Unit = {},
|
descriptorBuilder: ActionDescriptor.() -> Unit = {},
|
||||||
block: suspend (MetaItem<*>?) -> Any?
|
block: suspend (MetaItem<*>?) -> Any?
|
||||||
): ActionDelegate<D> = ActionDelegate(this, descriptorBuilder){
|
): ActionDelegate<D> = ActionDelegate(this, descriptorBuilder) {
|
||||||
val res = block(it)
|
val res = block(it)
|
||||||
MetaItem.ValueItem(Value.of(res))
|
MetaItem.ValueItem(Value.of(res))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <D : DeviceBase> D.requestMeta(
|
fun <D : DeviceBase> D.requestMeta(
|
||||||
descriptorBuilder: ActionDescriptor.()->Unit = {},
|
descriptorBuilder: ActionDescriptor.() -> Unit = {},
|
||||||
block: suspend MetaBuilder.(MetaItem<*>?) -> Unit
|
block: suspend MetaBuilder.(MetaItem<*>?) -> Unit
|
||||||
): ActionDelegate<D> = ActionDelegate(this, descriptorBuilder){
|
): ActionDelegate<D> = ActionDelegate(this, descriptorBuilder) {
|
||||||
val res = MetaBuilder().apply { block(it)}
|
val res = MetaBuilder().apply { block(it) }
|
||||||
MetaItem.NodeItem(res)
|
MetaItem.NodeItem(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <D : DeviceBase> D.action(
|
fun <D : DeviceBase> D.action(
|
||||||
descriptorBuilder: ActionDescriptor.()->Unit = {},
|
descriptorBuilder: ActionDescriptor.() -> Unit = {},
|
||||||
block: suspend (MetaItem<*>?) -> Unit
|
block: suspend (MetaItem<*>?) -> Unit
|
||||||
): ActionDelegate<D> = ActionDelegate(this, descriptorBuilder) {
|
): ActionDelegate<D> = ActionDelegate(this, descriptorBuilder) {
|
||||||
block(it)
|
block(it)
|
||||||
|
@ -5,6 +5,7 @@ import hep.dataforge.control.api.Device
|
|||||||
import hep.dataforge.control.api.DeviceListener
|
import hep.dataforge.control.api.DeviceListener
|
||||||
import hep.dataforge.control.api.PropertyDescriptor
|
import hep.dataforge.control.api.PropertyDescriptor
|
||||||
import hep.dataforge.meta.MetaItem
|
import hep.dataforge.meta.MetaItem
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Baseline implementation of [Device] interface
|
* Baseline implementation of [Device] interface
|
||||||
@ -23,8 +24,15 @@ abstract class DeviceBase : Device {
|
|||||||
listeners.removeAll { it.first == owner }
|
listeners.removeAll { it.first == owner }
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun propertyChanged(propertyName: String, value: MetaItem<*>?) {
|
fun notifyListeners(block: DeviceListener.() -> Unit) {
|
||||||
listeners.forEach { it.second.propertyChanged(propertyName, value) }
|
listeners.forEach { it.second.block() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notifyPropertyChanged(propertyName: String) {
|
||||||
|
scope.launch {
|
||||||
|
val value = getProperty(propertyName)
|
||||||
|
notifyListeners { propertyChanged(propertyName, value) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override val propertyDescriptors: Collection<PropertyDescriptor>
|
override val propertyDescriptors: Collection<PropertyDescriptor>
|
||||||
@ -54,8 +62,8 @@ abstract class DeviceBase : Device {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun execute(action: String, argument: MetaItem<*>?): MetaItem<*>? =
|
override suspend fun execute(command: String, argument: MetaItem<*>?): MetaItem<*>? =
|
||||||
(actions[action] ?: error("Request with name $action not defined")).invoke(argument)
|
(actions[command] ?: error("Request with name $command not defined")).invoke(argument)
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -18,6 +18,10 @@ import kotlinx.coroutines.withContext
|
|||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
import kotlin.reflect.KProperty
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
private fun DeviceBase.propertyChanged(name: String, item: MetaItem<*>?){
|
||||||
|
notifyListeners { propertyChanged(name, item) }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A stand-alone [ReadOnlyDeviceProperty] implementation not directly attached to a device
|
* A stand-alone [ReadOnlyDeviceProperty] implementation not directly attached to a device
|
||||||
*/
|
*/
|
||||||
@ -27,7 +31,7 @@ open class IsolatedReadOnlyDeviceProperty(
|
|||||||
default: MetaItem<*>?,
|
default: MetaItem<*>?,
|
||||||
override val descriptor: PropertyDescriptor,
|
override val descriptor: PropertyDescriptor,
|
||||||
override val scope: CoroutineScope,
|
override val scope: CoroutineScope,
|
||||||
private val updateCallback: (name: String, item: MetaItem<*>) -> Unit,
|
private val callback: (name: String, item: MetaItem<*>) -> Unit,
|
||||||
private val getter: suspend (before: MetaItem<*>?) -> MetaItem<*>
|
private val getter: suspend (before: MetaItem<*>?) -> MetaItem<*>
|
||||||
) : ReadOnlyDeviceProperty {
|
) : ReadOnlyDeviceProperty {
|
||||||
|
|
||||||
@ -40,7 +44,7 @@ open class IsolatedReadOnlyDeviceProperty(
|
|||||||
|
|
||||||
protected fun update(item: MetaItem<*>) {
|
protected fun update(item: MetaItem<*>) {
|
||||||
state.value = item
|
state.value = item
|
||||||
updateCallback(name, item)
|
callback(name, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun read(force: Boolean): MetaItem<*> {
|
override suspend fun read(force: Boolean): MetaItem<*> {
|
||||||
@ -62,6 +66,22 @@ open class IsolatedReadOnlyDeviceProperty(
|
|||||||
override fun flow(): StateFlow<MetaItem<*>?> = state
|
override fun flow(): StateFlow<MetaItem<*>?> = state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun DeviceBase.readOnlyProperty(
|
||||||
|
name: String,
|
||||||
|
default: MetaItem<*>?,
|
||||||
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
|
getter: suspend (MetaItem<*>?) -> MetaItem<*>
|
||||||
|
): ReadOnlyDeviceProperty = registerProperty(name) {
|
||||||
|
IsolatedReadOnlyDeviceProperty(
|
||||||
|
name,
|
||||||
|
default,
|
||||||
|
PropertyDescriptor(name).apply(descriptorBuilder),
|
||||||
|
scope,
|
||||||
|
::propertyChanged,
|
||||||
|
getter
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private class ReadOnlyDevicePropertyDelegate<D : DeviceBase>(
|
private class ReadOnlyDevicePropertyDelegate<D : DeviceBase>(
|
||||||
val owner: D,
|
val owner: D,
|
||||||
val default: MetaItem<*>?,
|
val default: MetaItem<*>?,
|
||||||
@ -176,6 +196,24 @@ class IsolatedDeviceProperty(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun DeviceBase.mutableProperty(
|
||||||
|
name: String,
|
||||||
|
default: MetaItem<*>?,
|
||||||
|
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
|
||||||
|
getter: suspend (MetaItem<*>?) -> MetaItem<*>,
|
||||||
|
setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?
|
||||||
|
): ReadOnlyDeviceProperty = registerProperty(name) {
|
||||||
|
IsolatedDeviceProperty(
|
||||||
|
name,
|
||||||
|
default,
|
||||||
|
PropertyDescriptor(name).apply(descriptorBuilder),
|
||||||
|
scope,
|
||||||
|
::propertyChanged,
|
||||||
|
getter,
|
||||||
|
setter
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private class DevicePropertyDelegate<D : DeviceBase>(
|
private class DevicePropertyDelegate<D : DeviceBase>(
|
||||||
val owner: D,
|
val owner: D,
|
||||||
val default: MetaItem<*>?,
|
val default: MetaItem<*>?,
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package hep.dataforge.control.controllers
|
package hep.dataforge.control.controllers
|
||||||
|
|
||||||
import hep.dataforge.control.api.Consumer
|
import hep.dataforge.control.api.*
|
||||||
import hep.dataforge.control.api.Device
|
|
||||||
import hep.dataforge.control.api.DeviceListener
|
|
||||||
import hep.dataforge.control.controllers.DeviceMessage.Companion.PROPERTY_CHANGED_ACTION
|
import hep.dataforge.control.controllers.DeviceMessage.Companion.PROPERTY_CHANGED_ACTION
|
||||||
import hep.dataforge.io.Envelope
|
import hep.dataforge.io.Envelope
|
||||||
import hep.dataforge.io.Responder
|
import hep.dataforge.io.Responder
|
||||||
import hep.dataforge.io.SimpleEnvelope
|
import hep.dataforge.io.SimpleEnvelope
|
||||||
import hep.dataforge.meta.MetaItem
|
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
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.consumeAsFlow
|
import kotlinx.coroutines.flow.consumeAsFlow
|
||||||
@ -26,13 +29,9 @@ class DeviceController(
|
|||||||
|
|
||||||
private val outputChannel = Channel<Envelope>(Channel.CONFLATED)
|
private val outputChannel = Channel<Envelope>(Channel.CONFLATED)
|
||||||
|
|
||||||
suspend fun respondMessage(message: DeviceMessage): DeviceMessage {
|
suspend fun respondMessage(message: DeviceMessage): DeviceMessage = respondMessage(device, deviceTarget, message)
|
||||||
return Device.respondMessage(device, deviceTarget, message)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun respond(request: Envelope): Envelope {
|
override suspend fun respond(request: Envelope): Envelope = respond(device, deviceTarget, request)
|
||||||
return Device.respond(device, deviceTarget, request)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun propertyChanged(propertyName: String, value: MetaItem<*>?) {
|
override fun propertyChanged(propertyName: String, value: MetaItem<*>?) {
|
||||||
if (value == null) return
|
if (value == null) return
|
||||||
@ -61,6 +60,116 @@ class DeviceController(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
const val GET_PROPERTY_ACTION = "read"
|
||||||
|
const val SET_PROPERTY_ACTION = "write"
|
||||||
|
const val EXECUTE_ACTION = "execute"
|
||||||
|
const val PROPERTY_LIST_ACTION = "propertyList"
|
||||||
|
const val ACTION_LIST_ACTION = "actionList"
|
||||||
|
|
||||||
|
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 {
|
||||||
|
val response = device.respond(request).apply {
|
||||||
|
meta {
|
||||||
|
"target" put request.meta["source"].string
|
||||||
|
"source" put deviceTarget
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response.build()
|
||||||
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
DeviceMessage.fail {
|
||||||
|
comment = ex.message
|
||||||
|
}.wrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun DeviceHub.respondMessage(request: DeviceMessage): DeviceMessage {
|
||||||
|
return try {
|
||||||
|
val targetName = request.target?.toName() ?: Name.EMPTY
|
||||||
|
val device = this[targetName]
|
||||||
|
DeviceController.respondMessage(device, targetName.toString(), request)
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
DeviceMessage.fail {
|
||||||
|
comment = ex.message
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package hep.dataforge.control.controllers
|
package hep.dataforge.control.controllers
|
||||||
|
|
||||||
import hep.dataforge.control.api.Device.Companion.GET_PROPERTY_ACTION
|
import hep.dataforge.control.controllers.DeviceController.Companion.GET_PROPERTY_ACTION
|
||||||
import hep.dataforge.io.SimpleEnvelope
|
import hep.dataforge.io.SimpleEnvelope
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
package hep.dataforge.control.controllers
|
package hep.dataforge.control.controllers
|
||||||
|
|
||||||
import hep.dataforge.control.api.*
|
import hep.dataforge.control.api.Consumer
|
||||||
|
import hep.dataforge.control.api.DeviceHub
|
||||||
|
import hep.dataforge.control.api.DeviceListener
|
||||||
|
import hep.dataforge.control.api.get
|
||||||
import hep.dataforge.io.Envelope
|
import hep.dataforge.io.Envelope
|
||||||
import hep.dataforge.io.Responder
|
import hep.dataforge.io.Responder
|
||||||
import hep.dataforge.meta.MetaItem
|
import hep.dataforge.meta.MetaItem
|
||||||
@ -60,7 +63,7 @@ class HubController(
|
|||||||
suspend fun respondMessage(message: DeviceMessage): DeviceMessage = try {
|
suspend fun respondMessage(message: DeviceMessage): DeviceMessage = try {
|
||||||
val targetName = message.target?.toName() ?: Name.EMPTY
|
val targetName = message.target?.toName() ?: Name.EMPTY
|
||||||
val device = hub[targetName]
|
val device = hub[targetName]
|
||||||
Device.respondMessage(device, targetName.toString(), message)
|
DeviceController.respondMessage(device, targetName.toString(), message)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
DeviceMessage.fail {
|
DeviceMessage.fail {
|
||||||
comment = ex.message
|
comment = ex.message
|
||||||
@ -71,9 +74,9 @@ class HubController(
|
|||||||
val targetName = request.meta[DeviceMessage.TARGET_KEY].string?.toName() ?: Name.EMPTY
|
val targetName = request.meta[DeviceMessage.TARGET_KEY].string?.toName() ?: Name.EMPTY
|
||||||
val device = hub[targetName]
|
val device = hub[targetName]
|
||||||
if (request.data == null) {
|
if (request.data == null) {
|
||||||
Device.respondMessage(device, targetName.toString(), DeviceMessage.wrap(request.meta)).wrap()
|
DeviceController.respondMessage(device, targetName.toString(), DeviceMessage.wrap(request.meta)).wrap()
|
||||||
} else {
|
} else {
|
||||||
Device.respond(device, targetName.toString(), request)
|
DeviceController.respond(device, targetName.toString(), request)
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
DeviceMessage.fail {
|
DeviceMessage.fail {
|
||||||
|
@ -2,7 +2,7 @@ package hep.dataforge.control.ports
|
|||||||
|
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.receiveAsFlow
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
@ -16,7 +16,12 @@ abstract class Port(val scope: CoroutineScope) : Closeable {
|
|||||||
|
|
||||||
private val outgoing = Channel<ByteArray>(100)
|
private val outgoing = Channel<ByteArray>(100)
|
||||||
private val incoming = Channel<ByteArray>(Channel.CONFLATED)
|
private val incoming = Channel<ByteArray>(Channel.CONFLATED)
|
||||||
//val receiveChannel: ReceiveChannel<ByteArray> get() = incoming
|
|
||||||
|
init {
|
||||||
|
scope.coroutineContext[Job]?.invokeOnCompletion {
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal method to synchronously send data
|
* Internal method to synchronously send data
|
||||||
@ -45,6 +50,9 @@ abstract class Port(val scope: CoroutineScope) : Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a data packet via the port
|
||||||
|
*/
|
||||||
suspend fun send(data: ByteArray) {
|
suspend fun send(data: ByteArray) {
|
||||||
outgoing.send(data)
|
outgoing.send(data)
|
||||||
}
|
}
|
||||||
@ -59,9 +67,9 @@ abstract class Port(val scope: CoroutineScope) : Closeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
scope.cancel("The port is closed")
|
|
||||||
outgoing.close()
|
outgoing.close()
|
||||||
incoming.close()
|
incoming.close()
|
||||||
|
sendJob.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
package hep.dataforge.control.server
|
package hep.dataforge.control.server
|
||||||
|
|
||||||
import hep.dataforge.control.api.Device.Companion.GET_PROPERTY_ACTION
|
|
||||||
import hep.dataforge.control.api.Device.Companion.SET_PROPERTY_ACTION
|
|
||||||
import hep.dataforge.control.api.get
|
import hep.dataforge.control.api.get
|
||||||
import hep.dataforge.control.api.respondMessage
|
import hep.dataforge.control.controllers.DeviceController.Companion.GET_PROPERTY_ACTION
|
||||||
|
import hep.dataforge.control.controllers.DeviceController.Companion.SET_PROPERTY_ACTION
|
||||||
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.control.controllers.data
|
import hep.dataforge.control.controllers.data
|
||||||
|
import hep.dataforge.control.controllers.respondMessage
|
||||||
import hep.dataforge.meta.toJson
|
import hep.dataforge.meta.toJson
|
||||||
import hep.dataforge.meta.toMeta
|
import hep.dataforge.meta.toMeta
|
||||||
import hep.dataforge.meta.toMetaItem
|
import hep.dataforge.meta.toMetaItem
|
||||||
|
Loading…
Reference in New Issue
Block a user