Dev #6

Merged
altavir merged 75 commits from dev into master 2021-10-23 11:02:48 +03:00
48 changed files with 242 additions and 151 deletions
Showing only changes of commit cb22374da5 - Show all commits
README.mdbuild.gradle.kts
controls-core
controls-magix-client
build.gradle.kts
src/commonMain/kotlin/hep/dataforge/control/client
controls-serial
build.gradle.kts
src/main/kotlin/hep/dataforge/control/serial
controls-server
build.gradle.kts
src/main/kotlin/hep/dataforge/control/server
controls-tcp
build.gradle.kts
src/jvmMain/kotlin/hep/dataforge/control/ports
demo
build.gradle.kts
src/main/kotlin/hep/dataforge/control/demo
magix
magix-java-client
build.gradle.kts
src/main
java/ru/mipt/npm/magix/client
kotlin/ru/mipt/npm/magix/client
magix-server
build.gradle.kts
src/main/kotlin/hep/dataforge/magix/server
motors
settings.gradle.kts

@ -31,7 +31,7 @@ Among other things, you can:
### `dataforge-control-core` module packages ### `dataforge-control-core` module packages
- `api` - defines API for device management. The main class here is - `api` - defines API for device management. The main class here is
[`Device`](dataforge-device-core/src/commonMain/kotlin/hep/dataforge/control/api/Device.kt). [`Device`](controls-core/src/commonMain/kotlin/hep/dataforge/control/api/Device.kt).
Generally, a Device has Properties that can be read and written. Also, some Actions Generally, a Device has Properties that can be read and written. Also, some Actions
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).

@ -4,28 +4,26 @@ plugins {
kotlin("js") apply false kotlin("js") apply false
} }
val dataforgeVersion: String by extra("0.2.1-dev-2") val dataforgeVersion: String by extra("0.3.0")
val ktorVersion: String by extra("1.5.0") val ktorVersion: String by extra(ru.mipt.npm.gradle.KScienceVersions.ktorVersion)
val rsocketVersion by extra("0.12.0") val rsocketVersion by extra("0.12.0")
allprojects { allprojects {
repositories { repositories {
mavenLocal() mavenLocal()
//maven("https://dl.bintray.com/pdvrieze/maven")
//maven("http://maven.jzy3d.org/releases") //maven("http://maven.jzy3d.org/releases")
maven("https://kotlin.bintray.com/js-externals") maven("https://kotlin.bintray.com/js-externals")
maven("https://maven.pkg.github.com/altavir/kotlin-logging/") maven("https://dl.bintray.com/rsocket-admin/RSocket")
//maven("https://dl.bintray.com/rsocket-admin/RSocket")
//maven("https://maven.pkg.github.com/altavir/ktor-client-sse") //maven("https://maven.pkg.github.com/altavir/ktor-client-sse")
} }
group = "hep.dataforge" group = "ru.mipt.npm"
version = "0.1.0" version = "0.1.0"
} }
ksciencePublish { ksciencePublish {
githubProject = "controls.kt" githubProject = "controls.kt"
bintrayRepo = "dataforge" bintrayRepo = "kscience"
} }
apiValidation { apiValidation {

@ -4,7 +4,6 @@ plugins {
} }
val dataforgeVersion: String by rootProject.extra val dataforgeVersion: String by rootProject.extra
val ktorVersion: String by rootProject.extra
kscience { kscience {
useCoroutines("1.4.1") useCoroutines("1.4.1")

@ -4,7 +4,7 @@ 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.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.misc.Type
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
@ -35,7 +35,7 @@ public interface Device : Closeable, ContextAware {
* 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
@ -46,18 +46,18 @@ public interface Device : Closeable, ContextAware {
* Set property [value] for a property with name [propertyName]. * Set property [value] for a property with name [propertyName].
* In rare cases could suspend if the [Device] supports command queue and it is full at the moment. * In rare cases could suspend if the [Device] supports command queue and it is full at the moment.
*/ */
public suspend fun setProperty(propertyName: String, value: MetaItem<*>) public suspend fun setProperty(propertyName: String, value: MetaItem)
/** /**
* The [SharedFlow] of property changes * The [SharedFlow] of property changes
*/ */
public val propertyFlow: SharedFlow<Pair<String, MetaItem<*>>> 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(action: 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")
@ -74,4 +74,4 @@ public suspend fun Device.getState(): Meta = Meta{
} }
} }
//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 { MetaItemNode(it) })

@ -54,14 +54,14 @@ public operator fun DeviceHub.get(name: Name): Device? = when {
public operator fun DeviceHub.get(deviceName: String): Device? = get(deviceName.toName()) public operator fun DeviceHub.get(deviceName: String): Device? = get(deviceName.toName())
public suspend fun DeviceHub.getProperty(deviceName: Name, propertyName: String): MetaItem<*>? = public suspend fun DeviceHub.getProperty(deviceName: Name, propertyName: String): MetaItem? =
this[deviceName]?.getProperty(propertyName) this[deviceName]?.getProperty(propertyName)
public suspend fun DeviceHub.setProperty(deviceName: Name, propertyName: String, value: MetaItem<*>) { public suspend fun DeviceHub.setProperty(deviceName: Name, propertyName: String, value: MetaItem) {
this[deviceName]?.setProperty(propertyName, value) this[deviceName]?.setProperty(propertyName, value)
} }
public suspend fun DeviceHub.execute(deviceName: Name, command: String, argument: MetaItem<*>?): MetaItem<*>? = public suspend fun DeviceHub.execute(deviceName: Name, command: String, argument: MetaItem?): MetaItem? =
this[deviceName]?.execute(command, argument) this[deviceName]?.execute(command, argument)

@ -8,7 +8,7 @@ import hep.dataforge.meta.asMetaItem
public interface DeviceAction { public interface DeviceAction {
public val name: String public val name: String
public val descriptor: ActionDescriptor public val descriptor: ActionDescriptor
public suspend operator fun invoke(arg: MetaItem<*>? = null): MetaItem<*>? public suspend operator fun invoke(arg: MetaItem? = null): MetaItem?
} }
public suspend operator fun DeviceAction.invoke(meta: Meta): MetaItem<*>? = invoke(meta.asMetaItem()) public suspend operator fun DeviceAction.invoke(meta: Meta): MetaItem? = invoke(meta.asMetaItem())

@ -4,8 +4,8 @@ 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.PropertyDescriptor import hep.dataforge.control.api.PropertyDescriptor
import hep.dataforge.meta.DFExperimental
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.misc.DFExperimental
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -29,9 +29,9 @@ 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 sharedPropertyFlow = MutableSharedFlow<Pair<String, MetaItem<*>>>() private val sharedPropertyFlow = MutableSharedFlow<Pair<String, MetaItem>>()
override val propertyFlow: SharedFlow<Pair<String, MetaItem<*>>> get() = sharedPropertyFlow override val propertyFlow: SharedFlow<Pair<String, MetaItem>> get() = sharedPropertyFlow
private val sharedLogFlow = MutableSharedFlow<LogEntry>() private val sharedLogFlow = MutableSharedFlow<LogEntry>()
@ -62,47 +62,47 @@ public abstract class DeviceBase(override val context: Context) : Device {
_actions[name] = action _actions[name] = action
} }
override suspend fun getProperty(propertyName: String): MetaItem<*> = override suspend fun getProperty(propertyName: String): MetaItem =
(_properties[propertyName] ?: error("Property with name $propertyName not defined")).read() (_properties[propertyName] ?: error("Property with name $propertyName not defined")).read()
override suspend fun invalidateProperty(propertyName: String) { override suspend fun invalidateProperty(propertyName: String) {
(_properties[propertyName] ?: error("Property with name $propertyName not defined")).invalidate() (_properties[propertyName] ?: error("Property with name $propertyName not defined")).invalidate()
} }
override suspend fun setProperty(propertyName: String, value: MetaItem<*>) { override suspend fun setProperty(propertyName: String, value: MetaItem) {
(_properties[propertyName] as? DeviceProperty ?: error("Property with name $propertyName not defined")).write( (_properties[propertyName] as? DeviceProperty ?: error("Property with name $propertyName not defined")).write(
value value
) )
} }
override suspend fun execute(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) (_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(
override val name: String, override val name: String,
default: MetaItem<*>?, default: MetaItem?,
override val descriptor: PropertyDescriptor, override val descriptor: PropertyDescriptor,
private val getter: suspend (before: MetaItem<*>?) -> MetaItem<*>, private val getter: suspend (before: MetaItem?) -> MetaItem,
) : ReadOnlyDeviceProperty { ) : ReadOnlyDeviceProperty {
override val scope: CoroutineScope get() = this@DeviceBase.scope override val scope: CoroutineScope get() = this@DeviceBase.scope
private val state: MutableStateFlow<MetaItem<*>?> = MutableStateFlow(default) private val state: MutableStateFlow<MetaItem?> = MutableStateFlow(default)
override val value: MetaItem<*>? get() = state.value override val value: MetaItem? get() = state.value
override suspend fun invalidate() { override suspend fun invalidate() {
state.value = null state.value = null
} }
override fun updateLogical(item: MetaItem<*>) { override fun updateLogical(item: MetaItem) {
state.value = item state.value = item
scope.launch { scope.launch {
sharedPropertyFlow.emit(Pair(name, item)) sharedPropertyFlow.emit(Pair(name, item))
} }
} }
override suspend fun read(force: Boolean): MetaItem<*> { override suspend fun read(force: Boolean): MetaItem {
//backup current value //backup current value
val currentValue = value val currentValue = value
return if (force || currentValue == null) { return if (force || currentValue == null) {
@ -118,7 +118,7 @@ public abstract class DeviceBase(override val context: Context) : Device {
} }
} }
override fun flow(): StateFlow<MetaItem<*>?> = state override fun flow(): StateFlow<MetaItem?> = state
} }
/** /**
@ -126,9 +126,9 @@ public abstract class DeviceBase(override val context: Context) : Device {
*/ */
public fun createReadOnlyProperty( public fun createReadOnlyProperty(
name: String, name: String,
default: MetaItem<*>?, default: MetaItem?,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>, getter: suspend (MetaItem?) -> MetaItem,
): ReadOnlyDeviceProperty { ): ReadOnlyDeviceProperty {
val property = BasicReadOnlyDeviceProperty( val property = BasicReadOnlyDeviceProperty(
name, name,
@ -143,13 +143,13 @@ public abstract class DeviceBase(override val context: Context) : Device {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
private inner class BasicDeviceProperty( private inner class BasicDeviceProperty(
name: String, name: String,
default: MetaItem<*>?, default: MetaItem?,
descriptor: PropertyDescriptor, descriptor: PropertyDescriptor,
getter: suspend (MetaItem<*>?) -> MetaItem<*>, getter: suspend (MetaItem?) -> MetaItem,
private val setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?, private val setter: suspend (oldValue: MetaItem?, newValue: MetaItem) -> MetaItem?,
) : BasicReadOnlyDeviceProperty(name, default, descriptor, getter), DeviceProperty { ) : BasicReadOnlyDeviceProperty(name, default, descriptor, getter), DeviceProperty {
override var value: MetaItem<*>? override var value: MetaItem?
get() = super.value get() = super.value
set(value) { set(value) {
scope.launch { scope.launch {
@ -163,7 +163,7 @@ public abstract class DeviceBase(override val context: Context) : Device {
private val writeLock = Mutex() private val writeLock = Mutex()
override suspend fun write(item: MetaItem<*>) { override suspend fun write(item: MetaItem) {
writeLock.withLock { writeLock.withLock {
//fast return if value is not changed //fast return if value is not changed
if (item == value) return@withLock if (item == value) return@withLock
@ -183,10 +183,10 @@ public abstract class DeviceBase(override val context: Context) : Device {
*/ */
internal fun createMutableProperty( internal fun createMutableProperty(
name: String, name: String,
default: MetaItem<*>?, default: MetaItem?,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>, getter: suspend (MetaItem?) -> MetaItem,
setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?, setter: suspend (oldValue: MetaItem?, newValue: MetaItem) -> MetaItem?,
): DeviceProperty { ): DeviceProperty {
val property = BasicDeviceProperty( val property = BasicDeviceProperty(
name, name,
@ -205,9 +205,9 @@ public abstract class DeviceBase(override val context: Context) : Device {
private inner class BasicDeviceAction( private inner class BasicDeviceAction(
override val name: String, override val name: String,
override val descriptor: ActionDescriptor, override val descriptor: ActionDescriptor,
private val block: suspend (MetaItem<*>?) -> MetaItem<*>?, private val block: suspend (MetaItem?) -> MetaItem?,
) : 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) block(arg)
} }
@ -219,7 +219,7 @@ public abstract class DeviceBase(override val context: Context) : Device {
internal fun createAction( internal fun createAction(
name: String, name: String,
descriptorBuilder: ActionDescriptor.() -> Unit = {}, descriptorBuilder: ActionDescriptor.() -> Unit = {},
block: suspend (MetaItem<*>?) -> MetaItem<*>?, block: suspend (MetaItem?) -> MetaItem?,
): DeviceAction { ): DeviceAction {
val action = BasicDeviceAction(name, ActionDescriptor(name).apply(descriptorBuilder), block) val action = BasicDeviceAction(name, ActionDescriptor(name).apply(descriptorBuilder), block)
registerAction(name, action) registerAction(name, action)

@ -30,24 +30,24 @@ public interface ReadOnlyDeviceProperty {
/** /**
* Directly update property logical value and notify listener without writing it to device * Directly update property logical value and notify listener without writing it to device
*/ */
public fun updateLogical(item: MetaItem<*>) public fun updateLogical(item: MetaItem)
/** /**
* Get cached value and return null if value is invalid or not initialized * Get cached value and return null if value is invalid or not initialized
*/ */
public val value: MetaItem<*>? public val value: MetaItem?
/** /**
* Read value either from cache if cache is valid or directly from physical device. * Read value either from cache if cache is valid or directly from physical device.
* If [force], reread from physical state even if the logical state is set. * If [force], reread from physical state even if the logical state is set.
*/ */
public suspend fun read(force: Boolean = false): MetaItem<*> public suspend fun read(force: Boolean = false): MetaItem
/** /**
* The [Flow] representing future logical states of the property. * The [Flow] representing future logical states of the property.
* Produces null when the state is invalidated * Produces null when the state is invalidated
*/ */
public fun flow(): Flow<MetaItem<*>?> public fun flow(): Flow<MetaItem?>
} }
@ -65,10 +65,10 @@ public fun ReadOnlyDeviceProperty.readEvery(duration: Duration): Job = scope.lau
* A writeable device property with non-suspended write * A writeable device property with non-suspended write
*/ */
public interface DeviceProperty : ReadOnlyDeviceProperty { public interface DeviceProperty : ReadOnlyDeviceProperty {
override var value: MetaItem<*>? override var value: MetaItem?
/** /**
* Write value to physical device. Invalidates logical value, but does not update it automatically * Write value to physical device. Invalidates logical value, but does not update it automatically
*/ */
public suspend fun write(item: MetaItem<*>) public suspend fun write(item: MetaItem)
} }

@ -32,7 +32,7 @@ public class TypedDeviceProperty<T : Any>(
converter: MetaConverter<T>, converter: MetaConverter<T>,
) : TypedReadOnlyDeviceProperty<T>(property, converter), DeviceProperty { ) : TypedReadOnlyDeviceProperty<T>(property, converter), DeviceProperty {
override var value: MetaItem<*>? override var value: MetaItem?
get() = property.value get() = property.value
set(arg) { set(arg) {
property.value = arg property.value = arg
@ -44,7 +44,7 @@ public class TypedDeviceProperty<T : Any>(
property.value = arg?.let { converter.objectToMetaItem(arg) } property.value = arg?.let { converter.objectToMetaItem(arg) }
} }
override suspend fun write(item: MetaItem<*>) { override suspend fun write(item: MetaItem) {
property.write(item) property.write(item)
} }

@ -3,6 +3,8 @@ package hep.dataforge.control.base
import hep.dataforge.control.api.ActionDescriptor import hep.dataforge.control.api.ActionDescriptor
import hep.dataforge.meta.MetaBuilder import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.MetaItemNode
import hep.dataforge.meta.MetaItemValue
import hep.dataforge.values.Value import hep.dataforge.values.Value
import kotlin.properties.PropertyDelegateProvider import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
@ -20,7 +22,7 @@ public typealias ActionDelegate = ReadOnlyProperty<DeviceBase, DeviceAction>
private class ActionProvider<D : DeviceBase>( private class ActionProvider<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?,
) : PropertyDelegateProvider<D, ActionDelegate> { ) : PropertyDelegateProvider<D, ActionDelegate> {
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): ActionDelegate { override operator fun provideDelegate(thisRef: D, property: KProperty<*>): ActionDelegate {
val name = property.name val name = property.name
@ -31,28 +33,28 @@ private class ActionProvider<D : DeviceBase>(
public fun DeviceBase.requesting( public fun DeviceBase.requesting(
descriptorBuilder: ActionDescriptor.() -> Unit = {}, descriptorBuilder: ActionDescriptor.() -> Unit = {},
action: suspend (MetaItem<*>?) -> MetaItem<*>?, action: suspend (MetaItem?) -> MetaItem?,
): PropertyDelegateProvider<DeviceBase, ActionDelegate> = ActionProvider(this, descriptorBuilder, action) ): PropertyDelegateProvider<DeviceBase, ActionDelegate> = ActionProvider(this, descriptorBuilder, action)
public fun <D : DeviceBase> D.requestingValue( public fun <D : DeviceBase> D.requestingValue(
descriptorBuilder: ActionDescriptor.() -> Unit = {}, descriptorBuilder: ActionDescriptor.() -> Unit = {},
action: suspend (MetaItem<*>?) -> Any?, action: suspend (MetaItem?) -> Any?,
): PropertyDelegateProvider<D, ActionDelegate> = ActionProvider(this, descriptorBuilder) { ): PropertyDelegateProvider<D, ActionDelegate> = ActionProvider(this, descriptorBuilder) {
val res = action(it) val res = action(it)
MetaItem.ValueItem(Value.of(res)) MetaItemValue(Value.of(res))
} }
public fun <D : DeviceBase> D.requestingMeta( public fun <D : DeviceBase> D.requestingMeta(
descriptorBuilder: ActionDescriptor.() -> Unit = {}, descriptorBuilder: ActionDescriptor.() -> Unit = {},
action: suspend MetaBuilder.(MetaItem<*>?) -> Unit, action: suspend MetaBuilder.(MetaItem?) -> Unit,
): PropertyDelegateProvider<D, ActionDelegate> = ActionProvider(this, descriptorBuilder) { ): PropertyDelegateProvider<D, ActionDelegate> = ActionProvider(this, descriptorBuilder) {
val res = MetaBuilder().apply { action(it) } val res = MetaBuilder().apply { action(it) }
MetaItem.NodeItem(res) MetaItemNode(res)
} }
public fun DeviceBase.acting( public fun DeviceBase.acting(
descriptorBuilder: ActionDescriptor.() -> Unit = {}, descriptorBuilder: ActionDescriptor.() -> Unit = {},
action: suspend (MetaItem<*>?) -> Unit, action: suspend (MetaItem?) -> Unit,
): PropertyDelegateProvider<DeviceBase, ActionDelegate> = ActionProvider(this, descriptorBuilder) { ): PropertyDelegateProvider<DeviceBase, ActionDelegate> = ActionProvider(this, descriptorBuilder) {
action(it) action(it)
null null

@ -29,9 +29,9 @@ public typealias TypedReadOnlyPropertyDelegate<T> = ReadOnlyProperty<DeviceBase,
private class ReadOnlyDevicePropertyProvider<D : DeviceBase>( private class ReadOnlyDevicePropertyProvider<D : DeviceBase>(
val owner: D, val owner: D,
val default: MetaItem<*>?, val default: MetaItem?,
val descriptorBuilder: PropertyDescriptor.() -> Unit = {}, val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>, private val getter: suspend (MetaItem?) -> MetaItem,
) : PropertyDelegateProvider<D, ReadOnlyPropertyDelegate> { ) : PropertyDelegateProvider<D, ReadOnlyPropertyDelegate> {
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): ReadOnlyPropertyDelegate { override operator fun provideDelegate(thisRef: D, property: KProperty<*>): ReadOnlyPropertyDelegate {
@ -43,10 +43,10 @@ private class ReadOnlyDevicePropertyProvider<D : DeviceBase>(
private class TypedReadOnlyDevicePropertyProvider<D : DeviceBase, T : Any>( private class TypedReadOnlyDevicePropertyProvider<D : DeviceBase, T : Any>(
val owner: D, val owner: D,
val default: MetaItem<*>?, val default: MetaItem?,
val converter: MetaConverter<T>, val converter: MetaConverter<T>,
val descriptorBuilder: PropertyDescriptor.() -> Unit = {}, val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>, private val getter: suspend (MetaItem?) -> MetaItem,
) : PropertyDelegateProvider<D, TypedReadOnlyPropertyDelegate<T>> { ) : PropertyDelegateProvider<D, TypedReadOnlyPropertyDelegate<T>> {
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): TypedReadOnlyPropertyDelegate<T> { override operator fun provideDelegate(thisRef: D, property: KProperty<*>): TypedReadOnlyPropertyDelegate<T> {
@ -57,9 +57,9 @@ private class TypedReadOnlyDevicePropertyProvider<D : DeviceBase, T : Any>(
} }
public fun DeviceBase.reading( public fun DeviceBase.reading(
default: MetaItem<*>? = null, default: MetaItem? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>, getter: suspend (MetaItem?) -> MetaItem,
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider( ): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
this, this,
default, default,
@ -73,9 +73,9 @@ public fun DeviceBase.readingValue(
getter: suspend () -> Any?, getter: suspend () -> Any?,
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider( ): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
this, this,
default?.let { MetaItem.ValueItem(it) }, default?.let { MetaItemValue(it) },
descriptorBuilder, descriptorBuilder,
getter = { MetaItem.ValueItem(Value.of(getter())) } getter = { MetaItemValue(Value.of(getter())) }
) )
public fun DeviceBase.readingNumber( public fun DeviceBase.readingNumber(
@ -84,12 +84,12 @@ public fun DeviceBase.readingNumber(
getter: suspend () -> Number, getter: suspend () -> Number,
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Number>> = TypedReadOnlyDevicePropertyProvider( ): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Number>> = TypedReadOnlyDevicePropertyProvider(
this, this,
default?.let { MetaItem.ValueItem(it.asValue()) }, default?.let { MetaItemValue(it.asValue()) },
MetaConverter.number, MetaConverter.number,
descriptorBuilder, descriptorBuilder,
getter = { getter = {
val number = getter() val number = getter()
MetaItem.ValueItem(number.asValue()) MetaItemValue(number.asValue())
} }
) )
@ -99,12 +99,12 @@ public fun DeviceBase.readingDouble(
getter: suspend () -> Double, getter: suspend () -> Double,
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Double>> = TypedReadOnlyDevicePropertyProvider( ): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Double>> = TypedReadOnlyDevicePropertyProvider(
this, this,
default?.let { MetaItem.ValueItem(it.asValue()) }, default?.let { MetaItemValue(it.asValue()) },
MetaConverter.double, MetaConverter.double,
descriptorBuilder, descriptorBuilder,
getter = { getter = {
val number = getter() val number = getter()
MetaItem.ValueItem(number.asValue()) MetaItemValue(number.asValue())
} }
) )
@ -114,12 +114,12 @@ public fun DeviceBase.readingString(
getter: suspend () -> String, getter: suspend () -> String,
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<String>> = TypedReadOnlyDevicePropertyProvider( ): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<String>> = TypedReadOnlyDevicePropertyProvider(
this, this,
default?.let { MetaItem.ValueItem(it.asValue()) }, default?.let { MetaItemValue(it.asValue()) },
MetaConverter.string, MetaConverter.string,
descriptorBuilder, descriptorBuilder,
getter = { getter = {
val number = getter() val number = getter()
MetaItem.ValueItem(number.asValue()) MetaItemValue(number.asValue())
} }
) )
@ -129,12 +129,12 @@ public fun DeviceBase.readingBoolean(
getter: suspend () -> Boolean, getter: suspend () -> Boolean,
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Boolean>> = TypedReadOnlyDevicePropertyProvider( ): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Boolean>> = TypedReadOnlyDevicePropertyProvider(
this, this,
default?.let { MetaItem.ValueItem(it.asValue()) }, default?.let { MetaItemValue(it.asValue()) },
MetaConverter.boolean, MetaConverter.boolean,
descriptorBuilder, descriptorBuilder,
getter = { getter = {
val boolean = getter() val boolean = getter()
MetaItem.ValueItem(boolean.asValue()) MetaItemValue(boolean.asValue())
} }
) )
@ -144,11 +144,11 @@ public fun DeviceBase.readingMeta(
getter: suspend MetaBuilder.() -> Unit, getter: suspend MetaBuilder.() -> Unit,
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Meta>> = TypedReadOnlyDevicePropertyProvider( ): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Meta>> = TypedReadOnlyDevicePropertyProvider(
this, this,
default?.let { MetaItem.NodeItem(it) }, default?.let { MetaItemNode(it) },
MetaConverter.meta, MetaConverter.meta,
descriptorBuilder, descriptorBuilder,
getter = { getter = {
MetaItem.NodeItem(MetaBuilder().apply { getter() }) MetaItemNode(MetaBuilder().apply { getter() })
} }
) )
@ -170,10 +170,10 @@ public typealias TypedPropertyDelegate<T> = ReadOnlyProperty<DeviceBase, TypedDe
private class DevicePropertyProvider<D : DeviceBase>( private class DevicePropertyProvider<D : DeviceBase>(
val owner: D, val owner: D,
val default: MetaItem<*>?, val default: MetaItem?,
val descriptorBuilder: PropertyDescriptor.() -> Unit = {}, val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>, private val getter: suspend (MetaItem?) -> MetaItem,
private val setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?, private val setter: suspend (oldValue: MetaItem?, newValue: MetaItem) -> MetaItem?,
) : PropertyDelegateProvider<D, PropertyDelegate> { ) : PropertyDelegateProvider<D, PropertyDelegate> {
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): PropertyDelegate { override operator fun provideDelegate(thisRef: D, property: KProperty<*>): PropertyDelegate {
@ -185,11 +185,11 @@ private class DevicePropertyProvider<D : DeviceBase>(
private class TypedDevicePropertyProvider<D : DeviceBase, T : Any>( private class TypedDevicePropertyProvider<D : DeviceBase, T : Any>(
val owner: D, val owner: D,
val default: MetaItem<*>?, val default: MetaItem?,
val converter: MetaConverter<T>, val converter: MetaConverter<T>,
val descriptorBuilder: PropertyDescriptor.() -> Unit = {}, val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>, private val getter: suspend (MetaItem?) -> MetaItem,
private val setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?, private val setter: suspend (oldValue: MetaItem?, newValue: MetaItem) -> MetaItem?,
) : PropertyDelegateProvider<D, TypedPropertyDelegate<T>> { ) : PropertyDelegateProvider<D, TypedPropertyDelegate<T>> {
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): TypedPropertyDelegate<T> { override operator fun provideDelegate(thisRef: D, property: KProperty<*>): TypedPropertyDelegate<T> {
@ -200,10 +200,10 @@ private class TypedDevicePropertyProvider<D : DeviceBase, T : Any>(
} }
public fun DeviceBase.writing( public fun DeviceBase.writing(
default: MetaItem<*>? = null, default: MetaItem? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>, getter: suspend (MetaItem?) -> MetaItem,
setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?, setter: suspend (oldValue: MetaItem?, newValue: MetaItem) -> MetaItem?,
): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = DevicePropertyProvider( ): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = DevicePropertyProvider(
this, this,
default, default,
@ -213,7 +213,7 @@ public fun DeviceBase.writing(
) )
public fun DeviceBase.writingVirtual( public fun DeviceBase.writingVirtual(
default: MetaItem<*>, default: MetaItem,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = writing( ): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = writing(
default, default,
@ -226,9 +226,9 @@ public fun DeviceBase.writingVirtual(
default: Value, default: Value,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = writing( ): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = writing(
MetaItem.ValueItem(default), MetaItemValue(default),
descriptorBuilder, descriptorBuilder,
getter = { it ?: MetaItem.ValueItem(default) }, getter = { it ?: MetaItemValue(default) },
setter = { _, newItem -> newItem } setter = { _, newItem -> newItem }
) )
@ -236,9 +236,9 @@ public fun DeviceBase.writingVirtual(
default: Meta, default: Meta,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}, descriptorBuilder: PropertyDescriptor.() -> Unit = {},
): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = writing( ): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = writing(
MetaItem.NodeItem(default), MetaItemNode(default),
descriptorBuilder, descriptorBuilder,
getter = { it ?: MetaItem.NodeItem(default) }, getter = { it ?: MetaItemNode(default) },
setter = { _, newItem -> newItem } setter = { _, newItem -> newItem }
) )
@ -247,17 +247,17 @@ public fun <D : DeviceBase> D.writingDouble(
getter: suspend (Double) -> Double, getter: suspend (Double) -> Double,
setter: suspend (oldValue: Double?, newValue: Double) -> Double?, setter: suspend (oldValue: Double?, newValue: Double) -> Double?,
): PropertyDelegateProvider<D, TypedPropertyDelegate<Double>> { ): PropertyDelegateProvider<D, TypedPropertyDelegate<Double>> {
val innerGetter: suspend (MetaItem<*>?) -> MetaItem<*> = { val innerGetter: suspend (MetaItem?) -> MetaItem = {
MetaItem.ValueItem(getter(it.double ?: Double.NaN).asValue()) MetaItemValue(getter(it.double ?: Double.NaN).asValue())
} }
val innerSetter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>? = { oldValue, newValue -> val innerSetter: suspend (oldValue: MetaItem?, newValue: MetaItem) -> MetaItem? = { oldValue, newValue ->
setter(oldValue.double, newValue.double ?: Double.NaN)?.asMetaItem() setter(oldValue.double, newValue.double ?: Double.NaN)?.asMetaItem()
} }
return TypedDevicePropertyProvider( return TypedDevicePropertyProvider(
this, this,
MetaItem.ValueItem(Double.NaN.asValue()), MetaItemValue(Double.NaN.asValue()),
MetaConverter.double, MetaConverter.double,
descriptorBuilder, descriptorBuilder,
innerGetter, innerGetter,
@ -270,18 +270,18 @@ public fun <D : DeviceBase> D.writingBoolean(
getter: suspend (Boolean?) -> Boolean, getter: suspend (Boolean?) -> Boolean,
setter: suspend (oldValue: Boolean?, newValue: Boolean) -> Boolean?, setter: suspend (oldValue: Boolean?, newValue: Boolean) -> Boolean?,
): PropertyDelegateProvider<D, TypedPropertyDelegate<Boolean>> { ): PropertyDelegateProvider<D, TypedPropertyDelegate<Boolean>> {
val innerGetter: suspend (MetaItem<*>?) -> MetaItem<*> = { val innerGetter: suspend (MetaItem?) -> MetaItem = {
MetaItem.ValueItem(getter(it.boolean).asValue()) MetaItemValue(getter(it.boolean).asValue())
} }
val innerSetter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>? = { oldValue, newValue -> val innerSetter: suspend (oldValue: MetaItem?, newValue: MetaItem) -> MetaItem? = { oldValue, newValue ->
setter(oldValue.boolean, newValue.boolean ?: error("Can't convert $newValue to boolean"))?.asValue() setter(oldValue.boolean, newValue.boolean ?: error("Can't convert $newValue to boolean"))?.asValue()
?.asMetaItem() ?.asMetaItem()
} }
return TypedDevicePropertyProvider( return TypedDevicePropertyProvider(
this, this,
MetaItem.ValueItem(Null), MetaItemValue(Null),
MetaConverter.boolean, MetaConverter.boolean,
descriptorBuilder, descriptorBuilder,
innerGetter, innerGetter,

@ -8,20 +8,20 @@ import kotlin.time.Duration
import kotlin.time.DurationUnit import kotlin.time.DurationUnit
import kotlin.time.toDuration import kotlin.time.toDuration
public fun Double.asMetaItem(): MetaItem.ValueItem = MetaItem.ValueItem(asValue()) public fun Double.asMetaItem(): MetaItemValue = MetaItemValue(asValue())
//TODO to be moved to DF //TODO to be moved to DF
public object DurationConverter : MetaConverter<Duration> { public object DurationConverter : MetaConverter<Duration> {
override fun itemToObject(item: MetaItem<*>): Duration = when (item) { override fun itemToObject(item: MetaItem): Duration = when (item) {
is MetaItem.NodeItem -> { is MetaItemNode -> {
val unit: DurationUnit = item.node["unit"].enum<DurationUnit>() ?: DurationUnit.SECONDS val unit: DurationUnit = item.node["unit"].enum<DurationUnit>() ?: DurationUnit.SECONDS
val value = item.node[Meta.VALUE_KEY].double ?: error("No value present for Duration") val value = item.node[Meta.VALUE_KEY].double ?: error("No value present for Duration")
value.toDuration(unit) value.toDuration(unit)
} }
is MetaItem.ValueItem -> item.value.double.toDuration(DurationUnit.SECONDS) is MetaItemValue -> item.value.double.toDuration(DurationUnit.SECONDS)
} }
override fun objectToMetaItem(obj: Duration): MetaItem<*> = obj.toDouble(DurationUnit.SECONDS).asMetaItem() override fun objectToMetaItem(obj: Duration): MetaItem = obj.toDouble(DurationUnit.SECONDS).asMetaItem()
} }
public val MetaConverter.Companion.duration: MetaConverter<Duration> get() = DurationConverter public val MetaConverter.Companion.duration: MetaConverter<Duration> get() = DurationConverter

@ -4,9 +4,9 @@ import hep.dataforge.control.api.Device
import hep.dataforge.control.api.DeviceHub import hep.dataforge.control.api.DeviceHub
import hep.dataforge.control.api.get import hep.dataforge.control.api.get
import hep.dataforge.control.messages.* import hep.dataforge.control.messages.*
import hep.dataforge.meta.DFExperimental
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.misc.DFExperimental
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -21,7 +21,7 @@ public class DeviceController(
public val deviceName: String, public val deviceName: String,
) { ) {
private val propertyChanges = device.propertyFlow.map { (propertyName: String, value: MetaItem<*>) -> private val propertyChanges = device.propertyFlow.map { (propertyName: String, value: MetaItem) ->
PropertyChangedMessage( PropertyChangedMessage(
sourceDevice = deviceName, sourceDevice = deviceName,
property = propertyName, property = propertyName,
@ -103,12 +103,12 @@ public class DeviceController(
val descriptionMeta = Meta { val descriptionMeta = Meta {
"properties" put { "properties" put {
device.propertyDescriptors.map { descriptor -> device.propertyDescriptors.map { descriptor ->
descriptor.name put descriptor.config descriptor.name put descriptor.toMeta()
} }
} }
"actions" put { "actions" put {
device.actionDescriptors.map { descriptor -> device.actionDescriptors.map { descriptor ->
descriptor.name put descriptor.config descriptor.name put descriptor.toMeta()
} }
} }
} }

@ -3,7 +3,7 @@ package hep.dataforge.control.controllers
import hep.dataforge.control.api.DeviceHub import hep.dataforge.control.api.DeviceHub
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.meta.DFExperimental import hep.dataforge.misc.DFExperimental
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.toName import hep.dataforge.names.toName
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
@ -33,7 +33,7 @@ public class HubController(
// private val listeners: Map<NameToken, DeviceListener> = hub.devices.mapValues { (deviceNameToken, 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(

@ -42,7 +42,7 @@ public sealed class DeviceMessage {
@SerialName("property.changed") @SerialName("property.changed")
public data class PropertyChangedMessage( public data class PropertyChangedMessage(
public val property: String, public val property: String,
public val value: MetaItem<*>?, public val value: MetaItem?,
override val sourceDevice: String, override val sourceDevice: String,
override val targetDevice: String? = null, override val targetDevice: String? = null,
override val comment: String? = null, override val comment: String? = null,
@ -55,7 +55,7 @@ public data class PropertyChangedMessage(
@SerialName("property.set") @SerialName("property.set")
public data class PropertySetMessage( public data class PropertySetMessage(
public val property: String, public val property: String,
public val value: MetaItem<*>?, public val value: MetaItem?,
override val sourceDevice: String? = null, override val sourceDevice: String? = null,
override val targetDevice: String, override val targetDevice: String,
override val comment: String? = null, override val comment: String? = null,
@ -104,7 +104,7 @@ public data class DescriptionMessage(
@SerialName("action.execute") @SerialName("action.execute")
public data class ActionExecuteMessage( public data class ActionExecuteMessage(
public val action: String, public val action: String,
public val argument: MetaItem<*>?, public val argument: MetaItem?,
override val sourceDevice: String? = null, override val sourceDevice: String? = null,
override val targetDevice: String, override val targetDevice: String,
override val comment: String? = null, override val comment: String? = null,
@ -117,7 +117,7 @@ public data class ActionExecuteMessage(
@SerialName("action.result") @SerialName("action.result")
public data class ActionResultMessage( public data class ActionResultMessage(
public val action: String, public val action: String,
public val result: MetaItem<*>?, public val result: MetaItem?,
override val sourceDevice: String, override val sourceDevice: String,
override val targetDevice: String? = null, override val targetDevice: String? = null,
override val comment: String? = null, override val comment: String? = null,
@ -154,7 +154,7 @@ public data class EmptyDeviceMessage(
@SerialName("log") @SerialName("log")
public data class DeviceLogMessage( public data class DeviceLogMessage(
val message: String, val message: String,
val data: MetaItem<*>? = null, val data: MetaItem? = null,
override val sourceDevice: String? = null, override val sourceDevice: String? = null,
override val targetDevice: String? = null, override val targetDevice: String? = null,
override val comment: String? = null, override val comment: String? = null,

@ -3,6 +3,7 @@ package hep.dataforge.control.ports
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.ContextAware import hep.dataforge.context.ContextAware
import hep.dataforge.context.Factory import hep.dataforge.context.Factory
import hep.dataforge.context.logger
import hep.dataforge.control.api.Socket import hep.dataforge.control.api.Socket
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel

@ -3,6 +3,7 @@ package hep.dataforge.control.ports
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.ContextAware import hep.dataforge.context.ContextAware
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.context.logger
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect

@ -1,7 +1,7 @@
package hep.dataforge.control.controllers package hep.dataforge.control.controllers
import hep.dataforge.control.base.* import hep.dataforge.control.base.*
import hep.dataforge.meta.* import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.transformations.MetaConverter import hep.dataforge.meta.transformations.MetaConverter
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
@ -12,7 +12,7 @@ import kotlin.time.Duration
/** /**
* Blocking read of the value * Blocking read of the value
*/ */
public operator fun ReadOnlyDeviceProperty.getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*> = public operator fun ReadOnlyDeviceProperty.getValue(thisRef: Any?, property: KProperty<*>): MetaItem =
runBlocking(scope.coroutineContext) { runBlocking(scope.coroutineContext) {
read() read()
} }
@ -22,7 +22,7 @@ public operator fun <T: Any> TypedReadOnlyDeviceProperty<T>.getValue(thisRef: An
readTyped() readTyped()
} }
public operator fun DeviceProperty.setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem<*>) { public operator fun DeviceProperty.setValue(thisRef: Any?, property: KProperty<*>, value: MetaItem) {
this.value = value this.value = value
} }

@ -1,6 +1,7 @@
package hep.dataforge.control.ports package hep.dataforge.control.ports
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.logger
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.get import hep.dataforge.meta.get
import hep.dataforge.meta.int import hep.dataforge.meta.int

@ -3,8 +3,6 @@ plugins {
id("ru.mipt.npm.publish") id("ru.mipt.npm.publish")
} }
val ktorVersion: String by rootProject.extra
kscience{ kscience{
useSerialization { useSerialization {
json() json()
@ -16,7 +14,7 @@ kotlin {
commonMain { commonMain {
dependencies { dependencies {
implementation(project(":magix:magix-service")) implementation(project(":magix:magix-service"))
implementation(project(":dataforge-device-core")) implementation(project(":controls-core"))
} }
} }
} }

@ -1,5 +1,6 @@
package hep.dataforge.control.client package hep.dataforge.control.client
import hep.dataforge.context.logger
import hep.dataforge.control.controllers.DeviceManager import hep.dataforge.control.controllers.DeviceManager
import hep.dataforge.control.controllers.respondMessage import hep.dataforge.control.controllers.respondMessage
import hep.dataforge.control.messages.DeviceMessage import hep.dataforge.control.messages.DeviceMessage

@ -4,6 +4,6 @@ plugins {
} }
dependencies{ dependencies{
api(project(":dataforge-device-core")) api(project(":controls-core"))
implementation("org.scream3r:jssc:2.8.0") implementation("org.scream3r:jssc:2.8.0")
} }

@ -7,8 +7,8 @@ val dataforgeVersion: String by rootProject.extra
val ktorVersion: String by rootProject.extra val ktorVersion: String by rootProject.extra
dependencies{ dependencies{
implementation(project(":dataforge-device-core")) implementation(project(":controls-core"))
implementation(project(":dataforge-device-tcp")) implementation(project(":controls-tcp"))
implementation("io.ktor:ktor-server-cio:$ktorVersion") implementation("io.ktor:ktor-server-cio:$ktorVersion")
implementation("io.ktor:ktor-websockets:$ktorVersion") implementation("io.ktor:ktor-websockets:$ktorVersion")
implementation("io.ktor:ktor-serialization:$ktorVersion") implementation("io.ktor:ktor-serialization:$ktorVersion")

@ -20,18 +20,19 @@ import io.ktor.http.HttpStatusCode
import io.ktor.request.receiveText import io.ktor.request.receiveText
import io.ktor.response.respond import io.ktor.response.respond
import io.ktor.response.respondRedirect import io.ktor.response.respondRedirect
import io.ktor.routing.* import io.ktor.routing.get
import io.ktor.routing.post
import io.ktor.routing.route
import io.ktor.routing.routing
import io.ktor.server.cio.CIO import io.ktor.server.cio.CIO
import io.ktor.server.engine.ApplicationEngine import io.ktor.server.engine.ApplicationEngine
import io.ktor.server.engine.embeddedServer import io.ktor.server.engine.embeddedServer
import io.ktor.util.KtorExperimentalAPI import io.ktor.util.KtorExperimentalAPI
import io.ktor.util.getValue import io.ktor.util.getValue
import io.ktor.websocket.WebSockets import io.ktor.websocket.WebSockets
import io.ktor.websocket.webSocket
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.collect
import kotlinx.html.* import kotlinx.html.*
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
@ -124,7 +125,7 @@ public fun Application.deviceModule(
li { li {
a(href = "../$deviceName/${property.name}/get") { +"${property.name}: " } a(href = "../$deviceName/${property.name}/get") { +"${property.name}: " }
code { code {
+property.config.toJson().toString() +property.toMeta().toJson().toString()
} }
} }
} }
@ -135,7 +136,7 @@ public fun Application.deviceModule(
li { li {
+("${action.name}: ") +("${action.name}: ")
code { code {
+action.config.toJson().toString() +action.toMeta().toJson().toString()
} }
} }
} }
@ -152,12 +153,12 @@ public fun Application.deviceModule(
put("target", name.toString()) put("target", name.toString())
put("properties", buildJsonArray { put("properties", buildJsonArray {
device.propertyDescriptors.forEach { descriptor -> device.propertyDescriptors.forEach { descriptor ->
add(descriptor.config.toJson()) add(descriptor.toMeta().toJson())
} }
}) })
put("actions", buildJsonArray { put("actions", buildJsonArray {
device.actionDescriptors.forEach { actionDescriptor -> device.actionDescriptors.forEach { actionDescriptor ->
add(actionDescriptor.config.toJson()) add(actionDescriptor.toMeta().toJson())
} }
}) })
} }

@ -8,7 +8,7 @@ kotlin {
sourceSets { sourceSets {
commonMain { commonMain {
dependencies { dependencies {
api(project(":dataforge-device-core")) api(project(":controls-core"))
api("io.ktor:ktor-network:$ktorVersion") api("io.ktor:ktor-network:$ktorVersion")
} }
} }

@ -17,9 +17,9 @@ repositories{
} }
dependencies{ dependencies{
implementation(project(":dataforge-device-core")) implementation(project(":controls-core"))
implementation(project(":dataforge-device-server")) implementation(project(":controls-server"))
implementation(project(":dataforge-magix-client")) implementation(project(":controls-magix-client"))
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")

@ -2,6 +2,7 @@ package hep.dataforge.control.demo
import hep.dataforge.context.ContextAware import hep.dataforge.context.ContextAware
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.context.logger
import io.ktor.server.engine.ApplicationEngine import io.ktor.server.engine.ApplicationEngine
import javafx.scene.Parent import javafx.scene.Parent
import javafx.scene.control.Slider import javafx.scene.control.Slider

@ -0,0 +1,10 @@
plugins {
java
id("ru.mipt.npm.jvm")
id("ru.mipt.npm.publish")
}
dependencies {
implementation(project(":magix:magix-service"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:${ru.mipt.npm.gradle.KScienceVersions.coroutinesVersion}")
}

@ -0,0 +1,26 @@
package ru.mipt.npm.magix.client;
import hep.dataforge.magix.api.MagixMessage;
import kotlinx.serialization.json.JsonElement;
import java.io.IOException;
import java.util.concurrent.Flow;
/**
* See https://github.com/waltz-controls/rfc/tree/master/2
*
* @param <T>
*/
public interface MagixClient<T> {
void broadcast(MagixMessage<T> msg) throws IOException;
Flow.Publisher<MagixMessage<T>> subscribe();
static MagixClient<JsonElement> rSocketTcp(String host, int port) {
return ControlsMagixClient.Companion.rSocketTcp(host, port);
}
static MagixClient<JsonElement> rSocketWs(String host, int port, String path) {
return ControlsMagixClient.Companion.rSocketWs(host, port, path);
}
}

@ -0,0 +1,42 @@
package ru.mipt.npm.magix.client
import hep.dataforge.magix.api.MagixEndpoint
import hep.dataforge.magix.api.MagixMessage
import hep.dataforge.magix.api.MagixMessageFilter
import hep.dataforge.magix.service.RSocketMagixEndpoint
import hep.dataforge.magix.service.withTcp
import kotlinx.coroutines.jdk9.asPublisher
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.JsonElement
import java.util.concurrent.Flow
public class ControlsMagixClient<T>(
private val endpoint: MagixEndpoint,
private val filter: MagixMessageFilter,
private val serializer: KSerializer<T>,
) : MagixClient<T> {
override fun broadcast(msg: MagixMessage<T>): Unit = runBlocking {
endpoint.broadcast(serializer, msg)
}
override fun subscribe(): Flow.Publisher<MagixMessage<T>> = endpoint.subscribe(serializer, filter).asPublisher()
public companion object {
public fun rSocketTcp(host: String, port: Int): ControlsMagixClient<JsonElement> {
val endpoint = runBlocking {
RSocketMagixEndpoint.withTcp(host, port)
}
return ControlsMagixClient(endpoint, MagixMessageFilter(), JsonElement.serializer())
}
public fun rSocketWs(host: String, port: Int, path: String = "/rsocket"): ControlsMagixClient<JsonElement> {
val endpoint = runBlocking {
RSocketMagixEndpoint.withWebSockets(host, port, path)
}
return ControlsMagixClient(endpoint, MagixMessageFilter(), JsonElement.serializer())
}
}
}

@ -11,8 +11,8 @@ kscience {
} }
val dataforgeVersion: String by rootProject.extra val dataforgeVersion: String by rootProject.extra
val ktorVersion: String by rootProject.extra
val rsocketVersion: String by rootProject.extra val rsocketVersion: String by rootProject.extra
val ktorVersion: String = ru.mipt.npm.gradle.KScienceVersions.ktorVersion
dependencies{ dependencies{
api(project(":magix:magix-api")) api(project(":magix:magix-api"))

@ -25,6 +25,7 @@ public fun CoroutineScope.startMagixServer(
buffer, buffer,
extraBufferCapacity = buffer extraBufferCapacity = buffer
) )
val tcpTransport = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().serverTransport(port = rawSocketPort) val tcpTransport = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().serverTransport(port = rawSocketPort)
RSocketServer().bind(tcpTransport, magixAcceptor(magixFlow)) RSocketServer().bind(tcpTransport, magixAcceptor(magixFlow))

@ -52,12 +52,14 @@ internal fun CoroutineScope.magixAcceptor(magixFlow: MutableSharedFlow<GenericMa
magixFlow.emit(message) magixFlow.emit(message)
} }
// bi-directional connection // bi-directional connection
requestChannel { _: Payload, input: Flow<Payload> -> requestChannel { request: Payload, input: Flow<Payload> ->
input.onEach { input.onEach {
magixFlow.emit(magixJson.decodeFromString(genericMessageSerializer, it.data.readText())) magixFlow.emit(magixJson.decodeFromString(genericMessageSerializer, it.data.readText()))
}.launchIn(this@magixAcceptor) }.launchIn(this@magixAcceptor)
magixFlow.map { message -> val filter = magixJson.decodeFromString(MagixMessageFilter.serializer(), request.data.readText())
magixFlow.filter(filter).map { message ->
val string = magixJson.encodeToString(genericMessageSerializer, message) val string = magixJson.encodeToString(genericMessageSerializer, message)
buildPayload { data(string) } buildPayload { data(string) }
} }

@ -21,7 +21,7 @@ kscience{
val ktorVersion: String by rootProject.extra val ktorVersion: String by rootProject.extra
dependencies { dependencies {
implementation(project(":dataforge-device-tcp")) implementation(project(":controls-tcp"))
implementation(project(":dataforge-magix-client")) implementation(project(":controls-magix-client"))
implementation("no.tornado:tornadofx:1.7.20") implementation("no.tornado:tornadofx:1.7.20")
} }

@ -3,6 +3,7 @@
package ru.mipt.npm.devices.pimotionmaster package ru.mipt.npm.devices.pimotionmaster
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.logger
import hep.dataforge.control.api.DeviceHub import hep.dataforge.control.api.DeviceHub
import hep.dataforge.control.api.PropertyDescriptor import hep.dataforge.control.api.PropertyDescriptor
import hep.dataforge.control.base.* import hep.dataforge.control.base.*

@ -2,6 +2,7 @@ package ru.mipt.npm.devices.pimotionmaster
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.ContextAware import hep.dataforge.context.ContextAware
import hep.dataforge.context.logger
import hep.dataforge.control.api.Socket import hep.dataforge.control.api.Socket
import hep.dataforge.control.ports.AbstractPort import hep.dataforge.control.ports.AbstractPort
import hep.dataforge.control.ports.withDelimiter import hep.dataforge.control.ports.withDelimiter

@ -1,9 +1,12 @@
package ru.mipt.npm.devices.pimotionmaster package ru.mipt.npm.devices.pimotionmaster
import hep.dataforge.context.logger
import hep.dataforge.control.api.Device import hep.dataforge.control.api.Device
import hep.dataforge.control.base.TypedDeviceProperty import hep.dataforge.control.base.TypedDeviceProperty
import hep.dataforge.control.base.TypedReadOnlyDeviceProperty import hep.dataforge.control.base.TypedReadOnlyDeviceProperty
import javafx.beans.property.* import javafx.beans.property.ObjectPropertyBase
import javafx.beans.property.Property
import javafx.beans.property.ReadOnlyProperty
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach

@ -1,6 +1,6 @@
pluginManagement { pluginManagement {
val kotlinVersion = "1.4.21" val kotlinVersion = "1.4.30"
val toolsVersion = "0.7.1" val toolsVersion = "0.7.6"
repositories { repositories {
mavenLocal() mavenLocal()
@ -27,18 +27,20 @@ pluginManagement {
rootProject.name = "controls.kt" rootProject.name = "controls.kt"
include( include(
":dataforge-device-core", ":controls-core",
":dataforge-device-tcp", ":controls-tcp",
":dataforge-device-serial", ":controls-serial",
":dataforge-device-server", ":controls-server",
":demo", ":demo",
":magix", ":magix",
":magix:magix-api", ":magix:magix-api",
":magix:magix-server", ":magix:magix-server",
":magix:magix-service", ":magix:magix-service",
":dataforge-magix-client", ":magix:magix-java-client",
":controls-magix-client",
":motors" ":motors"
) )
//includeBuild("../dataforge-core") //includeBuild("../dataforge-core")
//includeBuild("../plotly.kt") //includeBuild("../plotly.kt")
include("magix-java-client")