Java rsocket bindings

This commit is contained in:
Alexander Nozik 2021-02-09 16:39:39 +03:00
parent b883bece48
commit cb22374da5
48 changed files with 242 additions and 151 deletions

View File

@ -31,7 +31,7 @@ Among other things, you can:
### `dataforge-control-core` module packages
- `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
can optionally be applied on a device (may or may not affect properties).

View File

@ -4,28 +4,26 @@ plugins {
kotlin("js") apply false
}
val dataforgeVersion: String by extra("0.2.1-dev-2")
val ktorVersion: String by extra("1.5.0")
val dataforgeVersion: String by extra("0.3.0")
val ktorVersion: String by extra(ru.mipt.npm.gradle.KScienceVersions.ktorVersion)
val rsocketVersion by extra("0.12.0")
allprojects {
repositories {
mavenLocal()
//maven("https://dl.bintray.com/pdvrieze/maven")
//maven("http://maven.jzy3d.org/releases")
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")
}
group = "hep.dataforge"
group = "ru.mipt.npm"
version = "0.1.0"
}
ksciencePublish {
githubProject = "controls.kt"
bintrayRepo = "dataforge"
bintrayRepo = "kscience"
}
apiValidation {

View File

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

View File

@ -4,7 +4,7 @@ import hep.dataforge.context.ContextAware
import hep.dataforge.control.api.Device.Companion.DEVICE_TARGET
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem
import hep.dataforge.provider.Type
import hep.dataforge.misc.Type
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
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.
* 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
@ -46,18 +46,18 @@ public interface Device : Closeable, ContextAware {
* 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.
*/
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<*>>>
public val propertyFlow: SharedFlow<Pair<String, MetaItem>>
/**
* Send an action request and suspend caller while request is being processed.
* 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() {
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) })

View File

@ -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 suspend fun DeviceHub.getProperty(deviceName: Name, propertyName: String): MetaItem<*>? =
public suspend fun DeviceHub.getProperty(deviceName: Name, propertyName: String): MetaItem? =
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)
}
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)

View File

@ -8,7 +8,7 @@ import hep.dataforge.meta.asMetaItem
public interface DeviceAction {
public val name: String
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())

View File

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

View File

@ -30,24 +30,24 @@ public interface ReadOnlyDeviceProperty {
/**
* 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
*/
public val value: MetaItem<*>?
public val value: MetaItem?
/**
* 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.
*/
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.
* 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
*/
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
*/
public suspend fun write(item: MetaItem<*>)
public suspend fun write(item: MetaItem)
}

View File

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

View File

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

View File

@ -29,9 +29,9 @@ public typealias TypedReadOnlyPropertyDelegate<T> = ReadOnlyProperty<DeviceBase,
private class ReadOnlyDevicePropertyProvider<D : DeviceBase>(
val owner: D,
val default: MetaItem<*>?,
val default: MetaItem?,
val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>,
private val getter: suspend (MetaItem?) -> MetaItem,
) : PropertyDelegateProvider<D, 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>(
val owner: D,
val default: MetaItem<*>?,
val default: MetaItem?,
val converter: MetaConverter<T>,
val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>,
private val getter: suspend (MetaItem?) -> MetaItem,
) : PropertyDelegateProvider<D, 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(
default: MetaItem<*>? = null,
default: MetaItem? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>,
getter: suspend (MetaItem?) -> MetaItem,
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
this,
default,
@ -73,9 +73,9 @@ public fun DeviceBase.readingValue(
getter: suspend () -> Any?,
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
this,
default?.let { MetaItem.ValueItem(it) },
default?.let { MetaItemValue(it) },
descriptorBuilder,
getter = { MetaItem.ValueItem(Value.of(getter())) }
getter = { MetaItemValue(Value.of(getter())) }
)
public fun DeviceBase.readingNumber(
@ -84,12 +84,12 @@ public fun DeviceBase.readingNumber(
getter: suspend () -> Number,
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Number>> = TypedReadOnlyDevicePropertyProvider(
this,
default?.let { MetaItem.ValueItem(it.asValue()) },
default?.let { MetaItemValue(it.asValue()) },
MetaConverter.number,
descriptorBuilder,
getter = {
val number = getter()
MetaItem.ValueItem(number.asValue())
MetaItemValue(number.asValue())
}
)
@ -99,12 +99,12 @@ public fun DeviceBase.readingDouble(
getter: suspend () -> Double,
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Double>> = TypedReadOnlyDevicePropertyProvider(
this,
default?.let { MetaItem.ValueItem(it.asValue()) },
default?.let { MetaItemValue(it.asValue()) },
MetaConverter.double,
descriptorBuilder,
getter = {
val number = getter()
MetaItem.ValueItem(number.asValue())
MetaItemValue(number.asValue())
}
)
@ -114,12 +114,12 @@ public fun DeviceBase.readingString(
getter: suspend () -> String,
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<String>> = TypedReadOnlyDevicePropertyProvider(
this,
default?.let { MetaItem.ValueItem(it.asValue()) },
default?.let { MetaItemValue(it.asValue()) },
MetaConverter.string,
descriptorBuilder,
getter = {
val number = getter()
MetaItem.ValueItem(number.asValue())
MetaItemValue(number.asValue())
}
)
@ -129,12 +129,12 @@ public fun DeviceBase.readingBoolean(
getter: suspend () -> Boolean,
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Boolean>> = TypedReadOnlyDevicePropertyProvider(
this,
default?.let { MetaItem.ValueItem(it.asValue()) },
default?.let { MetaItemValue(it.asValue()) },
MetaConverter.boolean,
descriptorBuilder,
getter = {
val boolean = getter()
MetaItem.ValueItem(boolean.asValue())
MetaItemValue(boolean.asValue())
}
)
@ -144,11 +144,11 @@ public fun DeviceBase.readingMeta(
getter: suspend MetaBuilder.() -> Unit,
): PropertyDelegateProvider<DeviceBase, TypedReadOnlyPropertyDelegate<Meta>> = TypedReadOnlyDevicePropertyProvider(
this,
default?.let { MetaItem.NodeItem(it) },
default?.let { MetaItemNode(it) },
MetaConverter.meta,
descriptorBuilder,
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>(
val owner: D,
val default: MetaItem<*>?,
val default: MetaItem?,
val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>,
private val setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?,
private val getter: suspend (MetaItem?) -> MetaItem,
private val setter: suspend (oldValue: MetaItem?, newValue: MetaItem) -> MetaItem?,
) : PropertyDelegateProvider<D, 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>(
val owner: D,
val default: MetaItem<*>?,
val default: MetaItem?,
val converter: MetaConverter<T>,
val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>,
private val setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?,
private val getter: suspend (MetaItem?) -> MetaItem,
private val setter: suspend (oldValue: MetaItem?, newValue: MetaItem) -> MetaItem?,
) : PropertyDelegateProvider<D, 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(
default: MetaItem<*>? = null,
default: MetaItem? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>,
setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?,
getter: suspend (MetaItem?) -> MetaItem,
setter: suspend (oldValue: MetaItem?, newValue: MetaItem) -> MetaItem?,
): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = DevicePropertyProvider(
this,
default,
@ -213,7 +213,7 @@ public fun DeviceBase.writing(
)
public fun DeviceBase.writingVirtual(
default: MetaItem<*>,
default: MetaItem,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = writing(
default,
@ -226,9 +226,9 @@ public fun DeviceBase.writingVirtual(
default: Value,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = writing(
MetaItem.ValueItem(default),
MetaItemValue(default),
descriptorBuilder,
getter = { it ?: MetaItem.ValueItem(default) },
getter = { it ?: MetaItemValue(default) },
setter = { _, newItem -> newItem }
)
@ -236,9 +236,9 @@ public fun DeviceBase.writingVirtual(
default: Meta,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = writing(
MetaItem.NodeItem(default),
MetaItemNode(default),
descriptorBuilder,
getter = { it ?: MetaItem.NodeItem(default) },
getter = { it ?: MetaItemNode(default) },
setter = { _, newItem -> newItem }
)
@ -247,17 +247,17 @@ public fun <D : DeviceBase> D.writingDouble(
getter: suspend (Double) -> Double,
setter: suspend (oldValue: Double?, newValue: Double) -> Double?,
): PropertyDelegateProvider<D, TypedPropertyDelegate<Double>> {
val innerGetter: suspend (MetaItem<*>?) -> MetaItem<*> = {
MetaItem.ValueItem(getter(it.double ?: Double.NaN).asValue())
val innerGetter: suspend (MetaItem?) -> MetaItem = {
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()
}
return TypedDevicePropertyProvider(
this,
MetaItem.ValueItem(Double.NaN.asValue()),
MetaItemValue(Double.NaN.asValue()),
MetaConverter.double,
descriptorBuilder,
innerGetter,
@ -270,18 +270,18 @@ public fun <D : DeviceBase> D.writingBoolean(
getter: suspend (Boolean?) -> Boolean,
setter: suspend (oldValue: Boolean?, newValue: Boolean) -> Boolean?,
): PropertyDelegateProvider<D, TypedPropertyDelegate<Boolean>> {
val innerGetter: suspend (MetaItem<*>?) -> MetaItem<*> = {
MetaItem.ValueItem(getter(it.boolean).asValue())
val innerGetter: suspend (MetaItem?) -> MetaItem = {
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()
?.asMetaItem()
}
return TypedDevicePropertyProvider(
this,
MetaItem.ValueItem(Null),
MetaItemValue(Null),
MetaConverter.boolean,
descriptorBuilder,
innerGetter,

View File

@ -8,20 +8,20 @@ import kotlin.time.Duration
import kotlin.time.DurationUnit
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
public object DurationConverter : MetaConverter<Duration> {
override fun itemToObject(item: MetaItem<*>): Duration = when (item) {
is MetaItem.NodeItem -> {
override fun itemToObject(item: MetaItem): Duration = when (item) {
is MetaItemNode -> {
val unit: DurationUnit = item.node["unit"].enum<DurationUnit>() ?: DurationUnit.SECONDS
val value = item.node[Meta.VALUE_KEY].double ?: error("No value present for Duration")
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

View File

@ -4,9 +4,9 @@ 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.meta.DFExperimental
import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem
import hep.dataforge.misc.DFExperimental
import hep.dataforge.names.Name
import hep.dataforge.names.toName
import kotlinx.coroutines.flow.Flow
@ -21,7 +21,7 @@ public class DeviceController(
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(
sourceDevice = deviceName,
property = propertyName,
@ -103,12 +103,12 @@ public class DeviceController(
val descriptionMeta = Meta {
"properties" put {
device.propertyDescriptors.map { descriptor ->
descriptor.name put descriptor.config
descriptor.name put descriptor.toMeta()
}
}
"actions" put {
device.actionDescriptors.map { descriptor ->
descriptor.name put descriptor.config
descriptor.name put descriptor.toMeta()
}
}
}

View File

@ -3,7 +3,7 @@ package hep.dataforge.control.controllers
import hep.dataforge.control.api.DeviceHub
import hep.dataforge.control.api.get
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.toName
import kotlinx.coroutines.channels.Channel
@ -33,7 +33,7 @@ public class HubController(
// private val listeners: Map<NameToken, DeviceListener> = hub.devices.mapValues { (deviceNameToken, device) ->
// object : DeviceListener {
// override fun propertyChanged(propertyName: String, value: MetaItem<*>?) {
// override fun propertyChanged(propertyName: String, value: MetaItem?) {
// if (value == null) return
// scope.launch {
// val change = PropertyChangedMessage(

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
package hep.dataforge.control.controllers
import hep.dataforge.control.base.*
import hep.dataforge.meta.*
import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.transformations.MetaConverter
import kotlinx.coroutines.runBlocking
import kotlin.properties.ReadOnlyProperty
@ -12,7 +12,7 @@ import kotlin.time.Duration
/**
* 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) {
read()
}
@ -22,7 +22,7 @@ public operator fun <T: Any> TypedReadOnlyDeviceProperty<T>.getValue(thisRef: An
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
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,9 +17,9 @@ repositories{
}
dependencies{
implementation(project(":dataforge-device-core"))
implementation(project(":dataforge-device-server"))
implementation(project(":dataforge-magix-client"))
implementation(project(":controls-core"))
implementation(project(":controls-server"))
implementation(project(":controls-magix-client"))
implementation("no.tornado:tornadofx:1.7.20")
implementation(kotlin("stdlib-jdk8"))
implementation("kscience.plotlykt:plotlykt-server:0.3.0")

View File

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

View File

@ -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}")
}

View File

@ -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);
}
}

View File

@ -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())
}
}
}

View File

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

View File

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

View File

@ -52,12 +52,14 @@ internal fun CoroutineScope.magixAcceptor(magixFlow: MutableSharedFlow<GenericMa
magixFlow.emit(message)
}
// bi-directional connection
requestChannel { _: Payload, input: Flow<Payload> ->
requestChannel { request: Payload, input: Flow<Payload> ->
input.onEach {
magixFlow.emit(magixJson.decodeFromString(genericMessageSerializer, it.data.readText()))
}.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)
buildPayload { data(string) }
}

View File

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

View File

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

View File

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

View File

@ -1,9 +1,12 @@
package ru.mipt.npm.devices.pimotionmaster
import hep.dataforge.context.logger
import hep.dataforge.control.api.Device
import hep.dataforge.control.base.TypedDeviceProperty
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.launchIn
import kotlinx.coroutines.flow.onEach

View File

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