A lot of small refactoring in html
This commit is contained in:
parent
17beb29217
commit
d68f5a9840
@ -4,7 +4,7 @@ plugins {
|
|||||||
kotlin("js") apply false
|
kotlin("js") apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion: String by extra("0.2.0")
|
val dataforgeVersion: String by extra("0.2.1-dev-2")
|
||||||
val ktorVersion: String by extra("1.4.3")
|
val ktorVersion: String by extra("1.4.3")
|
||||||
val rsocketVersion by extra("0.11.1")
|
val rsocketVersion by extra("0.11.1")
|
||||||
|
|
||||||
|
@ -2,13 +2,12 @@ package hep.dataforge.control.api
|
|||||||
|
|
||||||
import hep.dataforge.context.ContextAware
|
import hep.dataforge.context.ContextAware
|
||||||
import hep.dataforge.control.api.Device.Companion.DEVICE_TARGET
|
import hep.dataforge.control.api.Device.Companion.DEVICE_TARGET
|
||||||
import hep.dataforge.io.Envelope
|
|
||||||
import hep.dataforge.io.EnvelopeBuilder
|
|
||||||
import hep.dataforge.meta.Meta
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.meta.MetaItem
|
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
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.io.Closeable
|
import kotlinx.io.Closeable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,27 +27,15 @@ public interface Device : Closeable, ContextAware {
|
|||||||
public val actionDescriptors: Collection<ActionDescriptor>
|
public val actionDescriptors: Collection<ActionDescriptor>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The supervisor scope encompassing all operations on a device. When canceled, cancels all running processes
|
* The supervisor scope encompassing all operations on a device. When canceled, cancels all running processes.
|
||||||
*/
|
*/
|
||||||
public val scope: CoroutineScope
|
public val scope: CoroutineScope
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a new property change listener for this device.
|
|
||||||
* [owner] is provided optionally in order for listener to be
|
|
||||||
* easily removable
|
|
||||||
*/
|
|
||||||
public fun registerListener(listener: DeviceListener, owner: Any? = listener)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all listeners belonging to the specified owner
|
|
||||||
*/
|
|
||||||
public fun removeListeners(owner: Any?)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the value of the property or throw error if property in not defined.
|
* Get the value of the property or throw error if property in not defined.
|
||||||
* Suspend if property value is not available
|
* Suspend if property value is not available
|
||||||
*/
|
*/
|
||||||
public suspend fun getProperty(propertyName: String): MetaItem<*>
|
public suspend fun getProperty(propertyName: String): MetaItem<*>?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate property and force recalculate
|
* Invalidate property and force recalculate
|
||||||
@ -61,11 +48,16 @@ public interface Device : Closeable, ContextAware {
|
|||||||
*/
|
*/
|
||||||
public suspend fun setProperty(propertyName: String, value: MetaItem<*>)
|
public suspend fun setProperty(propertyName: String, value: MetaItem<*>)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [SharedFlow] of property changes
|
||||||
|
*/
|
||||||
|
public val propertyFlow: SharedFlow<Pair<String, MetaItem<*>>>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
public suspend fun execute(command: String, argument: MetaItem<*>? = null): MetaItem<*>?
|
public suspend fun execute(action: String, argument: MetaItem<*>? = null): MetaItem<*>?
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
scope.cancel("The device is closed")
|
scope.cancel("The device is closed")
|
||||||
@ -76,14 +68,10 @@ public interface Device : Closeable, ContextAware {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ResponderDevice{
|
public suspend fun Device.getState(): Meta = Meta{
|
||||||
/**
|
for(descriptor in propertyDescriptors) {
|
||||||
*
|
descriptor.name put getProperty(descriptor.name)
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public suspend fun respondWithData(request: Envelope): EnvelopeBuilder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public suspend fun Device.execute(name: String, meta: Meta?): MetaItem<*>? = execute(name, meta?.let { MetaItem.NodeItem(it) })
|
//public suspend fun Device.execute(name: String, meta: Meta?): MetaItem<*>? = execute(name, meta?.let { MetaItem.NodeItem(it) })
|
@ -1,14 +0,0 @@
|
|||||||
package hep.dataforge.control.api
|
|
||||||
|
|
||||||
import hep.dataforge.meta.MetaItem
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PropertyChangeListener Interface
|
|
||||||
* [value] is a new value that property has after a change; null is for invalid state.
|
|
||||||
*/
|
|
||||||
public interface DeviceListener {
|
|
||||||
public fun propertyChanged(propertyName: String, value: MetaItem<*>?)
|
|
||||||
public fun actionExecuted(action: String, argument: MetaItem<*>?, result: MetaItem<*>?) {}
|
|
||||||
|
|
||||||
//TODO add general message listener method
|
|
||||||
}
|
|
@ -3,18 +3,25 @@ package hep.dataforge.control.base
|
|||||||
import hep.dataforge.context.Context
|
import hep.dataforge.context.Context
|
||||||
import hep.dataforge.control.api.ActionDescriptor
|
import hep.dataforge.control.api.ActionDescriptor
|
||||||
import hep.dataforge.control.api.Device
|
import hep.dataforge.control.api.Device
|
||||||
import hep.dataforge.control.api.DeviceListener
|
|
||||||
import hep.dataforge.control.api.PropertyDescriptor
|
import hep.dataforge.control.api.PropertyDescriptor
|
||||||
|
import hep.dataforge.meta.DFExperimental
|
||||||
import hep.dataforge.meta.MetaItem
|
import hep.dataforge.meta.MetaItem
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
|
||||||
|
//TODO move to DataForge-core
|
||||||
|
@DFExperimental
|
||||||
|
public data class LogEntry(val content: String, val priority: Int = 0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Baseline implementation of [Device] interface
|
* Baseline implementation of [Device] interface
|
||||||
*/
|
*/
|
||||||
|
@Suppress("EXPERIMENTAL_API_USAGE")
|
||||||
public abstract class DeviceBase(override val context: Context) : Device {
|
public abstract class DeviceBase(override val context: Context) : Device {
|
||||||
|
|
||||||
private val _properties = HashMap<String, ReadOnlyDeviceProperty>()
|
private val _properties = HashMap<String, ReadOnlyDeviceProperty>()
|
||||||
@ -22,25 +29,21 @@ public abstract class DeviceBase(override val context: Context) : Device {
|
|||||||
private val _actions = HashMap<String, DeviceAction>()
|
private val _actions = HashMap<String, DeviceAction>()
|
||||||
public val actions: Map<String, DeviceAction> get() = _actions
|
public val actions: Map<String, DeviceAction> get() = _actions
|
||||||
|
|
||||||
private val listeners = ArrayList<Pair<Any?, DeviceListener>>(4)
|
private val sharedPropertyFlow = MutableSharedFlow<Pair<String, MetaItem<*>>>()
|
||||||
|
|
||||||
override fun registerListener(listener: DeviceListener, owner: Any?) {
|
override val propertyFlow: SharedFlow<Pair<String, MetaItem<*>>> get() = sharedPropertyFlow
|
||||||
listeners.add(owner to listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun removeListeners(owner: Any?) {
|
private val sharedLogFlow = MutableSharedFlow<LogEntry>()
|
||||||
listeners.removeAll { it.first == owner }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun notifyListeners(block: DeviceListener.() -> Unit) {
|
/**
|
||||||
listeners.forEach { it.second.block() }
|
* The [SharedFlow] of log messages
|
||||||
}
|
*/
|
||||||
|
@DFExperimental
|
||||||
|
public val logFlow: SharedFlow<LogEntry>
|
||||||
|
get() = sharedLogFlow
|
||||||
|
|
||||||
public fun notifyPropertyChanged(propertyName: String) {
|
protected suspend fun log(message: String, priority: Int = 0) {
|
||||||
scope.launch {
|
sharedLogFlow.emit(LogEntry(message, priority))
|
||||||
val value = getProperty(propertyName)
|
|
||||||
notifyListeners { propertyChanged(propertyName, value) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override val propertyDescriptors: Collection<PropertyDescriptor>
|
override val propertyDescriptors: Collection<PropertyDescriptor>
|
||||||
@ -72,8 +75,8 @@ public abstract class DeviceBase(override val context: Context) : Device {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun execute(command: String, argument: MetaItem<*>?): MetaItem<*>? =
|
override suspend fun execute(action: String, argument: MetaItem<*>?): MetaItem<*>? =
|
||||||
(_actions[command] ?: error("Request with name $command not defined")).invoke(argument)
|
(_actions[action] ?: error("Request with name $action not defined")).invoke(argument)
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
private open inner class BasicReadOnlyDeviceProperty(
|
private open inner class BasicReadOnlyDeviceProperty(
|
||||||
@ -94,8 +97,8 @@ public abstract class DeviceBase(override val context: Context) : Device {
|
|||||||
|
|
||||||
override fun updateLogical(item: MetaItem<*>) {
|
override fun updateLogical(item: MetaItem<*>) {
|
||||||
state.value = item
|
state.value = item
|
||||||
notifyListeners {
|
scope.launch {
|
||||||
propertyChanged(name, item)
|
sharedPropertyFlow.emit(Pair(name, item))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,11 +209,7 @@ public abstract class DeviceBase(override val context: Context) : Device {
|
|||||||
) : DeviceAction {
|
) : DeviceAction {
|
||||||
override suspend fun invoke(arg: MetaItem<*>?): MetaItem<*>? =
|
override suspend fun invoke(arg: MetaItem<*>?): MetaItem<*>? =
|
||||||
withContext(scope.coroutineContext + SupervisorJob(scope.coroutineContext[Job])) {
|
withContext(scope.coroutineContext + SupervisorJob(scope.coroutineContext[Job])) {
|
||||||
block(arg).also {
|
block(arg)
|
||||||
notifyListeners {
|
|
||||||
actionExecuted(name, arg, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,62 +1,42 @@
|
|||||||
package hep.dataforge.control.controllers
|
package hep.dataforge.control.controllers
|
||||||
|
|
||||||
import hep.dataforge.control.api.*
|
import hep.dataforge.control.api.Device
|
||||||
|
import hep.dataforge.control.api.DeviceHub
|
||||||
|
import hep.dataforge.control.api.get
|
||||||
import hep.dataforge.control.messages.*
|
import hep.dataforge.control.messages.*
|
||||||
import hep.dataforge.io.Consumer
|
import hep.dataforge.meta.DFExperimental
|
||||||
import hep.dataforge.io.Envelope
|
import hep.dataforge.meta.Meta
|
||||||
import hep.dataforge.io.Responder
|
import hep.dataforge.meta.MetaItem
|
||||||
import hep.dataforge.io.SimpleEnvelope
|
|
||||||
import hep.dataforge.meta.*
|
|
||||||
import hep.dataforge.names.Name
|
import hep.dataforge.names.Name
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.consumeAsFlow
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.io.Binary
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The [DeviceController] wraps device operations in [DeviceMessage]
|
||||||
|
*/
|
||||||
@OptIn(DFExperimental::class)
|
@OptIn(DFExperimental::class)
|
||||||
public class DeviceController(
|
public class DeviceController(
|
||||||
public val device: Device,
|
public val device: Device,
|
||||||
public val deviceName: String,
|
public val deviceName: String,
|
||||||
public val scope: CoroutineScope = device.scope,
|
) {
|
||||||
) : Responder, Consumer, DeviceListener {
|
|
||||||
|
|
||||||
init {
|
private val propertyChanges = device.propertyFlow.map { (propertyName: String, value: MetaItem<*>) ->
|
||||||
device.registerListener(this, this)
|
PropertyChangedMessage(
|
||||||
|
sourceDevice = deviceName,
|
||||||
|
key = propertyName,
|
||||||
|
value = value,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val outputChannel = Channel<Envelope>(Channel.CONFLATED)
|
/**
|
||||||
|
* The flow of outgoing messages
|
||||||
|
*/
|
||||||
|
public val messages: Flow<DeviceMessage> get() = propertyChanges
|
||||||
|
|
||||||
public suspend fun respondMessage(message: DeviceMessage): DeviceMessage =
|
public suspend fun respondMessage(message: DeviceMessage): DeviceMessage =
|
||||||
respondMessage(device, deviceName, message)
|
respondMessage(device, deviceName, message)
|
||||||
|
|
||||||
override suspend fun respond(request: Envelope): Envelope = respond(device, deviceName, request)
|
|
||||||
|
|
||||||
override fun propertyChanged(propertyName: String, value: MetaItem<*>?) {
|
|
||||||
if (value == null) return
|
|
||||||
scope.launch {
|
|
||||||
val change = PropertyChangedMessage(
|
|
||||||
sourceDevice = deviceName,
|
|
||||||
key = propertyName,
|
|
||||||
value = value,
|
|
||||||
)
|
|
||||||
val envelope = SimpleEnvelope(change.toMeta(), Binary.EMPTY)
|
|
||||||
|
|
||||||
outputChannel.send(envelope)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public fun receiving(): Flow<Envelope> = outputChannel.consumeAsFlow()
|
|
||||||
|
|
||||||
@DFExperimental
|
|
||||||
override fun consume(message: Envelope) {
|
|
||||||
// Fire the respond procedure and forget about the result
|
|
||||||
scope.launch {
|
|
||||||
respond(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
public const val GET_PROPERTY_ACTION: String = "read"
|
public const val GET_PROPERTY_ACTION: String = "read"
|
||||||
@ -65,29 +45,21 @@ public class DeviceController(
|
|||||||
public const val PROPERTY_LIST_ACTION: String = "propertyList"
|
public const val PROPERTY_LIST_ACTION: String = "propertyList"
|
||||||
public const val ACTION_LIST_ACTION: String = "actionList"
|
public const val ACTION_LIST_ACTION: String = "actionList"
|
||||||
|
|
||||||
internal suspend fun respond(device: Device, deviceTarget: String, request: Envelope): Envelope {
|
// internal suspend fun respond(device: Device, deviceTarget: String, request: Envelope): Envelope {
|
||||||
val target = request.meta["target"].string
|
// val target = request.meta["target"].string
|
||||||
return try {
|
// return try {
|
||||||
if (request.data == null) {
|
// if (device is Responder) {
|
||||||
respondMessage(device, deviceTarget, DeviceMessage.fromMeta(request.meta)).toEnvelope()
|
// device.respond(request)
|
||||||
} else if (target != null && target != deviceTarget) {
|
// } else if (request.data == null) {
|
||||||
error("Wrong target name $deviceTarget expected but $target found")
|
// respondMessage(device, deviceTarget, DeviceMessage.fromMeta(request.meta)).toEnvelope()
|
||||||
} else {
|
// } else if (target != null && target != deviceTarget) {
|
||||||
if (device is ResponderDevice) {
|
// error("Wrong target name $deviceTarget expected but $target found")
|
||||||
val response = device.respondWithData(request).apply {
|
// } else error("Device does not support binary response")
|
||||||
meta {
|
// } catch (ex: Exception) {
|
||||||
"target" put request.meta["source"].string
|
// val requestSourceName = request.meta[DeviceMessage.SOURCE_KEY].string
|
||||||
"source" put deviceTarget
|
// DeviceMessage.error(ex, sourceDevice = deviceTarget, targetDevice = requestSourceName).toEnvelope()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
response.seal()
|
|
||||||
} else error("Device does not support binary response")
|
|
||||||
}
|
|
||||||
} catch (ex: Exception) {
|
|
||||||
val requestSourceName = request.meta[DeviceMessage.SOURCE_KEY].string
|
|
||||||
DeviceMessage.error(ex, sourceDevice = deviceTarget, targetDevice = requestSourceName).toEnvelope()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal suspend fun respondMessage(
|
internal suspend fun respondMessage(
|
||||||
device: Device,
|
device: Device,
|
||||||
|
@ -1,67 +1,53 @@
|
|||||||
package hep.dataforge.control.controllers
|
package hep.dataforge.control.controllers
|
||||||
|
|
||||||
import hep.dataforge.control.api.DeviceHub
|
import hep.dataforge.control.api.DeviceHub
|
||||||
import hep.dataforge.control.api.DeviceListener
|
|
||||||
import hep.dataforge.control.api.get
|
import hep.dataforge.control.api.get
|
||||||
import hep.dataforge.control.messages.DeviceMessage
|
import hep.dataforge.control.messages.DeviceMessage
|
||||||
import hep.dataforge.control.messages.PropertyChangedMessage
|
|
||||||
import hep.dataforge.control.messages.toEnvelope
|
|
||||||
import hep.dataforge.io.Consumer
|
|
||||||
import hep.dataforge.io.Envelope
|
|
||||||
import hep.dataforge.io.Responder
|
|
||||||
import hep.dataforge.meta.DFExperimental
|
import hep.dataforge.meta.DFExperimental
|
||||||
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.NameToken
|
|
||||||
import hep.dataforge.names.toName
|
import hep.dataforge.names.toName
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.consumeAsFlow
|
import kotlinx.coroutines.flow.consumeAsFlow
|
||||||
import kotlinx.coroutines.isActive
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
|
|
||||||
@OptIn(DFExperimental::class)
|
@OptIn(DFExperimental::class)
|
||||||
public class HubController(
|
public class HubController(
|
||||||
public val hub: DeviceHub,
|
public val hub: DeviceHub,
|
||||||
public val scope: CoroutineScope,
|
) {
|
||||||
) : Consumer, Responder {
|
|
||||||
|
|
||||||
private val messageOutbox = Channel<DeviceMessage>(Channel.CONFLATED)
|
private val messageOutbox = Channel<DeviceMessage>(Channel.CONFLATED)
|
||||||
|
|
||||||
private val envelopeOutbox = Channel<Envelope>(Channel.CONFLATED)
|
// private val envelopeOutbox = Channel<Envelope>(Channel.CONFLATED)
|
||||||
|
|
||||||
public fun messageOutput(): Flow<DeviceMessage> = messageOutbox.consumeAsFlow()
|
public fun messageOutput(): Flow<DeviceMessage> = messageOutbox.consumeAsFlow()
|
||||||
|
|
||||||
public fun envelopeOutput(): Flow<Envelope> = envelopeOutbox.consumeAsFlow()
|
// public fun envelopeOutput(): Flow<Envelope> = envelopeOutbox.consumeAsFlow()
|
||||||
|
|
||||||
private val packJob = scope.launch {
|
// private val packJob = scope.launch {
|
||||||
while (isActive) {
|
// while (isActive) {
|
||||||
val message = messageOutbox.receive()
|
// val message = messageOutbox.receive()
|
||||||
envelopeOutbox.send(message.toEnvelope())
|
// envelopeOutbox.send(message.toEnvelope())
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
private val listeners: Map<NameToken, DeviceListener> = hub.devices.mapValues { (name, device) ->
|
// private val listeners: Map<NameToken, DeviceListener> = hub.devices.mapValues { (deviceNameToken, device) ->
|
||||||
object : DeviceListener {
|
// object : DeviceListener {
|
||||||
override fun propertyChanged(propertyName: String, value: MetaItem<*>?) {
|
// override fun propertyChanged(propertyName: String, value: MetaItem<*>?) {
|
||||||
if (value == null) return
|
// if (value == null) return
|
||||||
scope.launch {
|
// scope.launch {
|
||||||
val change = PropertyChangedMessage(
|
// val change = PropertyChangedMessage(
|
||||||
sourceDevice = name.toString(),
|
// sourceDevice = deviceNameToken.toString(),
|
||||||
key = propertyName,
|
// key = propertyName,
|
||||||
value = value
|
// value = value
|
||||||
)
|
// )
|
||||||
messageOutbox.send(change)
|
// messageOutbox.send(change)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}.also {
|
// }.also {
|
||||||
device.registerListener(it)
|
// device.registerListener(it)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
public suspend fun respondMessage(message: DeviceMessage): DeviceMessage = try {
|
public suspend fun respondMessage(message: DeviceMessage): DeviceMessage = try {
|
||||||
val targetName = message.targetDevice?.toName() ?: Name.EMPTY
|
val targetName = message.targetDevice?.toName() ?: Name.EMPTY
|
||||||
@ -70,24 +56,24 @@ public class HubController(
|
|||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
DeviceMessage.error(ex, sourceDevice = null, targetDevice = message.sourceDevice)
|
DeviceMessage.error(ex, sourceDevice = null, targetDevice = message.sourceDevice)
|
||||||
}
|
}
|
||||||
|
//
|
||||||
override suspend fun respond(request: Envelope): Envelope = try {
|
// override suspend fun respond(request: Envelope): Envelope = try {
|
||||||
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] ?: error("The device with name $targetName not found in $hub")
|
// val device = hub[targetName] ?: error("The device with name $targetName not found in $hub")
|
||||||
if (request.data == null) {
|
// if (request.data == null) {
|
||||||
DeviceController.respondMessage(device, targetName.toString(), DeviceMessage.fromMeta(request.meta))
|
// DeviceController.respondMessage(device, targetName.toString(), DeviceMessage.fromMeta(request.meta))
|
||||||
.toEnvelope()
|
// .toEnvelope()
|
||||||
} else {
|
// } else {
|
||||||
DeviceController.respond(device, targetName.toString(), request)
|
// DeviceController.respond(device, targetName.toString(), request)
|
||||||
}
|
// }
|
||||||
} catch (ex: Exception) {
|
// } catch (ex: Exception) {
|
||||||
DeviceMessage.error(ex, sourceDevice = null).toEnvelope()
|
// DeviceMessage.error(ex, sourceDevice = null).toEnvelope()
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
override fun consume(message: Envelope) {
|
// override fun consume(message: Envelope) {
|
||||||
// Fire the respond procedure and forget about the result
|
// // Fire the respond procedure and forget about the result
|
||||||
scope.launch {
|
// scope.launch {
|
||||||
respond(message)
|
// respond(message)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
@ -1,30 +0,0 @@
|
|||||||
package hep.dataforge.control.controllers
|
|
||||||
|
|
||||||
import hep.dataforge.control.api.Device
|
|
||||||
import hep.dataforge.control.api.DeviceListener
|
|
||||||
import hep.dataforge.meta.MetaItem
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
|
||||||
import kotlinx.coroutines.flow.callbackFlow
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flow changes of all properties of a given device ignoring invalidation events
|
|
||||||
*/
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
public suspend fun Device.flowPropertyChanges(): Flow<Pair<String, MetaItem<*>>> = callbackFlow {
|
|
||||||
val listener = object : DeviceListener {
|
|
||||||
override fun propertyChanged(propertyName: String, value: MetaItem<*>?) {
|
|
||||||
if (value != null) {
|
|
||||||
launch {
|
|
||||||
send(propertyName to value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
registerListener(listener)
|
|
||||||
awaitClose {
|
|
||||||
removeListeners(listener)
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,8 +2,6 @@ package hep.dataforge.control.messages
|
|||||||
|
|
||||||
import hep.dataforge.io.SimpleEnvelope
|
import hep.dataforge.io.SimpleEnvelope
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.Name
|
|
||||||
import hep.dataforge.names.asName
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
@ -18,9 +16,6 @@ public sealed class DeviceMessage{
|
|||||||
|
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
public val SOURCE_KEY: Name = DeviceMessage::sourceDevice.name.asName()
|
|
||||||
public val TARGET_KEY: Name = DeviceMessage::targetDevice.name.asName()
|
|
||||||
|
|
||||||
public fun error(
|
public fun error(
|
||||||
cause: Throwable,
|
cause: Throwable,
|
||||||
sourceDevice: String?,
|
sourceDevice: String?,
|
||||||
@ -131,6 +126,18 @@ public data class EmptyDeviceMessage(
|
|||||||
override val comment: String? = null,
|
override val comment: String? = null,
|
||||||
) : DeviceMessage()
|
) : DeviceMessage()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information log message
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
@SerialName("log")
|
||||||
|
public data class DeviceLogMessage(
|
||||||
|
val message: String,
|
||||||
|
override val sourceDevice: String? = null,
|
||||||
|
override val targetDevice: String? = null,
|
||||||
|
override val comment: String? = null,
|
||||||
|
) : DeviceMessage()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The evaluation of the message produced a service error
|
* The evaluation of the message produced a service error
|
||||||
*/
|
*/
|
||||||
|
@ -3,10 +3,6 @@ plugins {
|
|||||||
id("ru.mipt.npm.publish")
|
id("ru.mipt.npm.publish")
|
||||||
}
|
}
|
||||||
|
|
||||||
kscience {
|
|
||||||
useSerialization()
|
|
||||||
}
|
|
||||||
|
|
||||||
val dataforgeVersion: String by rootProject.extra
|
val dataforgeVersion: String by rootProject.extra
|
||||||
val ktorVersion: String by rootProject.extra
|
val ktorVersion: String by rootProject.extra
|
||||||
|
|
||||||
|
@ -2,13 +2,8 @@ plugins {
|
|||||||
id("ru.mipt.npm.mpp")
|
id("ru.mipt.npm.mpp")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
val ktorVersion: String by rootProject.extra
|
val ktorVersion: String by rootProject.extra
|
||||||
|
|
||||||
kscience{
|
|
||||||
useCoroutines()
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlin {
|
kotlin {
|
||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
|
@ -23,6 +23,8 @@ dependencies{
|
|||||||
implementation("no.tornado:tornadofx:1.7.20")
|
implementation("no.tornado:tornadofx:1.7.20")
|
||||||
implementation(kotlin("stdlib-jdk8"))
|
implementation(kotlin("stdlib-jdk8"))
|
||||||
implementation("kscience.plotlykt:plotlykt-server:0.3.0")
|
implementation("kscience.plotlykt:plotlykt-server:0.3.0")
|
||||||
|
|
||||||
|
implementation("com.github.Ricky12Awesome:json-schema-serialization:0.6.6")
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
||||||
@ -37,5 +39,5 @@ javafx{
|
|||||||
}
|
}
|
||||||
|
|
||||||
application{
|
application{
|
||||||
mainClassName = "hep.dataforge.control.demo.DemoControllerViewKt"
|
mainClass.set("hep.dataforge.control.demo.DemoControllerViewKt")
|
||||||
}
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package hep.dataforge.control.demo
|
||||||
|
|
||||||
|
import com.github.ricky12awesome.jss.encodeToSchema
|
||||||
|
import com.github.ricky12awesome.jss.globalJson
|
||||||
|
import hep.dataforge.control.messages.DeviceMessage
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
val schema = globalJson.encodeToSchema(DeviceMessage.serializer(), generateDefinitions = false)
|
||||||
|
println(schema)
|
||||||
|
}
|
@ -7,7 +7,6 @@ kscience {
|
|||||||
useSerialization{
|
useSerialization{
|
||||||
json()
|
json()
|
||||||
}
|
}
|
||||||
useCoroutines("1.4.0", configuration = ru.mipt.npm.gradle.DependencyConfiguration.API)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion: String by rootProject.extra
|
val dataforgeVersion: String by rootProject.extra
|
||||||
|
@ -7,7 +7,6 @@ kscience {
|
|||||||
useSerialization{
|
useSerialization{
|
||||||
json()
|
json()
|
||||||
}
|
}
|
||||||
useCoroutines("1.4.1", configuration = ru.mipt.npm.gradle.DependencyConfiguration.API)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val dataforgeVersion: String by rootProject.extra
|
val dataforgeVersion: String by rootProject.extra
|
||||||
|
Loading…
Reference in New Issue
Block a user