Add alternative device syntax

This commit is contained in:
Alexander Nozik 2021-06-25 19:57:04 +03:00
parent e182af403f
commit fe3958fd08
11 changed files with 327 additions and 131 deletions

View File

@ -12,10 +12,11 @@ import space.kscience.dataforge.misc.Type
/**
* General interface describing a managed Device
* General interface describing a managed Device.
* Device is a supervisor scope encompassing all operations on a device. When canceled, cancels all running processes.
*/
@Type(DEVICE_TARGET)
public interface Device : Closeable, ContextAware {
public interface Device : Closeable, ContextAware, CoroutineScope {
/**
* List of supported property descriptors
*/
@ -27,11 +28,6 @@ public interface Device : Closeable, ContextAware {
*/
public val actionDescriptors: Collection<ActionDescriptor>
/**
* The supervisor scope encompassing all operations on a device. When canceled, cancels all running processes.
*/
public val scope: CoroutineScope
/**
* Get the value of the property or throw error if property in not defined.
* Suspend if property value is not available
@ -61,7 +57,7 @@ public interface Device : Closeable, ContextAware {
public suspend fun execute(action: String, argument: MetaItem? = null): MetaItem?
override fun close() {
scope.cancel("The device is closed")
cancel("The device is closed")
}
public companion object {

View File

@ -13,6 +13,7 @@ import ru.mipt.npm.controls.api.PropertyDescriptor
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.meta.MetaItem
import space.kscience.dataforge.misc.DFExperimental
import kotlin.coroutines.CoroutineContext
//TODO move to DataForge-core
@DFExperimental
@ -28,7 +29,7 @@ private open class BasicReadOnlyDeviceProperty(
private val getter: suspend (before: MetaItem?) -> MetaItem,
) : ReadOnlyDeviceProperty {
override val scope: CoroutineScope get() = device.scope
override val scope: CoroutineScope get() = device
private val state: MutableStateFlow<MetaItem?> = MutableStateFlow(default)
override val value: MetaItem? get() = state.value
@ -107,11 +108,11 @@ private class BasicDeviceProperty(
* Baseline implementation of [Device] interface
*/
@Suppress("EXPERIMENTAL_API_USAGE")
public abstract class DeviceBase(override val context: Context) : Device {
public abstract class DeviceBase(final override val context: Context) : Device {
override val coroutineContext: CoroutineContext =
context.coroutineContext + SupervisorJob(context.coroutineContext[Job])
override val scope: CoroutineScope by lazy {
CoroutineScope(context.coroutineContext + Job(context.coroutineContext[Job]))
}
private val _properties = HashMap<String, ReadOnlyDeviceProperty>()
public val properties: Map<String, ReadOnlyDeviceProperty> get() = _properties
@ -219,7 +220,7 @@ public abstract class DeviceBase(override val context: Context) : Device {
private val block: suspend (MetaItem?) -> MetaItem?,
) : DeviceAction {
override suspend fun invoke(arg: MetaItem?): MetaItem? =
withContext(scope.coroutineContext + SupervisorJob(scope.coroutineContext[Job])) {
withContext(coroutineContext) {
block(arg)
}
}

View File

@ -40,16 +40,15 @@ public class DeviceManager(override val deviceName: String = "") : AbstractPlugi
}
}
public interface DeviceSpec<D : Device> : Factory<D>
public fun <D : Device> DeviceManager.install(name: String, factory: DeviceSpec<D>, meta: Meta = Meta.EMPTY): D {
public fun <D : Device> DeviceManager.install(name: String, factory: Factory<D>, meta: Meta = Meta.EMPTY): D {
val device = factory(meta, context)
registerDevice(NameToken(name), device)
return device
}
public fun <D : Device> DeviceManager.installing(
factory: DeviceSpec<D>,
factory: Factory<D>,
metaBuilder: MetaBuilder.() -> Unit = {},
): ReadOnlyProperty<Any?, D> = ReadOnlyProperty { _, property ->
val name = property.name

View File

@ -1,11 +1,8 @@
package ru.mipt.npm.controls.properties
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import ru.mipt.npm.controls.api.ActionDescriptor
@ -15,22 +12,28 @@ import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.Global
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaItem
import space.kscience.dataforge.meta.transformations.MetaConverter
import kotlin.coroutines.CoroutineContext
import kotlin.properties.Delegates.observable
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* @param D recursive self-type for properties and actions
*/
public open class DeviceBySpec<D : DeviceBySpec<D>> : Device {
override var context: Context = Global
public open class DeviceBySpec<D : DeviceBySpec<D>>(
public val spec: DeviceSpec<D>,
context: Context = Global,
meta: Meta = Meta.EMPTY
) : Device {
override var context: Context = context
internal set
public var meta: Meta = Meta.EMPTY
public var meta: Meta = meta
internal set
public var properties: Map<String, DevicePropertySpec<D, *>> = emptyMap()
internal set
public var actions: Map<String, DeviceActionSpec<D, *, *>> = emptyMap()
internal set
public val properties: Map<String, DevicePropertySpec<D, *>> get() = spec.properties
public val actions: Map<String, DeviceActionSpec<D, *, *>> get() = spec.actions
override val propertyDescriptors: Collection<PropertyDescriptor>
get() = properties.values.map { it.descriptor }
@ -38,7 +41,9 @@ public open class DeviceBySpec<D : DeviceBySpec<D>> : Device {
override val actionDescriptors: Collection<ActionDescriptor>
get() = actions.values.map { it.descriptor }
override val scope: CoroutineScope get() = context
override val coroutineContext: CoroutineContext by lazy {
context.coroutineContext + SupervisorJob(context.coroutineContext[Job])
}
private val logicalState: HashMap<String, MetaItem?> = HashMap()
@ -54,7 +59,7 @@ public open class DeviceBySpec<D : DeviceBySpec<D>> : Device {
private val stateLock = Mutex()
internal suspend fun setLogicalState(propertyName: String, value: MetaItem?) {
internal suspend fun updateLogical(propertyName: String, value: MetaItem?) {
if (value != logicalState[propertyName]) {
stateLock.withLock {
logicalState[propertyName] = value
@ -66,12 +71,13 @@ public open class DeviceBySpec<D : DeviceBySpec<D>> : Device {
}
/**
* Force read physical value and push an update if it is changed
* Force read physical value and push an update if it is changed. It does not matter if logical state is present.
* The logical state is updated after read
*/
public suspend fun readProperty(propertyName: String): MetaItem {
val newValue = properties[propertyName]?.readItem(self)
?: error("A property with name $propertyName is not registered in $this")
setLogicalState(propertyName, newValue)
updateLogical(propertyName, newValue)
return newValue
}
@ -84,31 +90,58 @@ public open class DeviceBySpec<D : DeviceBySpec<D>> : Device {
}
}
override suspend fun setProperty(propertyName: String, value: MetaItem) {
override suspend fun setProperty(propertyName: String, value: MetaItem): Unit {
//If there is a physical property with given name, invalidate logical property and write physical one
(properties[propertyName] as? WritableDevicePropertySpec<D, out Any>)?.let {
it.writeItem(self, value)
invalidateProperty(propertyName)
} ?: run {
setLogicalState(propertyName, value)
updateLogical(propertyName, value)
}
}
override suspend fun execute(action: String, argument: MetaItem?): MetaItem? =
actions[action]?.executeItem(self, argument)
}
public operator fun <D : DeviceBySpec<D>, T : Any> D.get(
propertySpec: DevicePropertySpec<D, T>
): Deferred<T> = scope.async {
propertySpec.read(this@get).also {
setLogicalState(propertySpec.name, propertySpec.converter.objectToMetaItem(it))
/**
* A delegate that represents the logical-only state of the device
*/
public fun <T : Any> state(
converter: MetaConverter<T>,
initialValue: T,
): ReadWriteProperty<D, T> = observable(initialValue) { property: KProperty<*>, oldValue: T, newValue: T ->
if (oldValue != newValue) {
launch {
invalidateProperty(property.name)
_propertyFlow.emit(property.name to converter.objectToMetaItem(newValue))
}
}
}
public suspend fun <T : Any> DevicePropertySpec<D, T>.read(): T = read(self)
override fun close() {
with(spec){ self.onShutdown() }
super.close()
}
}
public suspend fun <D : DeviceBySpec<D>, T : Any> D.getSuspend(
propertySpec: DevicePropertySpec<D, T>
): T = propertySpec.read(this@getSuspend).also {
updateLogical(propertySpec.name, propertySpec.converter.objectToMetaItem(it))
}
public fun <D : DeviceBySpec<D>, T : Any> D.getAsync(
propertySpec: DevicePropertySpec<D, T>
): Deferred<T> = async {
getSuspend(propertySpec)
}
public operator fun <D : DeviceBySpec<D>, T : Any> D.set(propertySpec: WritableDevicePropertySpec<D, T>, value: T) {
scope.launch {
launch {
propertySpec.write(this@set, value)
invalidateProperty(propertySpec.name)
}

View File

@ -1,6 +1,6 @@
package ru.mipt.npm.controls.properties
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.withContext
import ru.mipt.npm.controls.api.ActionDescriptor
import ru.mipt.npm.controls.api.PropertyDescriptor
import space.kscience.dataforge.context.Context
@ -9,13 +9,58 @@ import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.transformations.MetaConverter
import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1
public abstract class DeviceSpec<D : DeviceBySpec<D>>(
private val buildDevice: () -> D
) : Factory<D> {
private val deviceProperties = HashMap<String, DevicePropertySpec<D, *>>()
private val deviceActions = HashMap<String, DeviceActionSpec<D, *, *>>()
private val _properties = HashMap<String, DevicePropertySpec<D, *>>()
public val properties: Map<String, DevicePropertySpec<D, *>> get() = _properties
private val _actions = HashMap<String, DeviceActionSpec<D, *, *>>()
public val actions: Map<String, DeviceActionSpec<D, *, *>> get() = _actions
public fun <T : Any> registerProperty(deviceProperty: DevicePropertySpec<D, T>): DevicePropertySpec<D, T> {
_properties[deviceProperty.name] = deviceProperty
return deviceProperty
}
public fun <T : Any> registerProperty(
converter: MetaConverter<T>,
readOnlyProperty: KProperty1<D, T>,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}
): DevicePropertySpec<D, T> {
val deviceProperty = object : DevicePropertySpec<D, T> {
override val name: String = readOnlyProperty.name
override val descriptor: PropertyDescriptor = PropertyDescriptor(this.name).apply(descriptorBuilder)
override val converter: MetaConverter<T> = converter
override suspend fun read(device: D): T =
withContext(device.coroutineContext) { readOnlyProperty.get(device) }
}
return registerProperty(deviceProperty)
}
public fun <T : Any> registerProperty(
converter: MetaConverter<T>,
readWriteProperty: KMutableProperty1<D, T>,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}
): WritableDevicePropertySpec<D, T> {
val deviceProperty = object : WritableDevicePropertySpec<D, T> {
override val name: String = readWriteProperty.name
override val descriptor: PropertyDescriptor = PropertyDescriptor(this.name).apply(descriptorBuilder)
override val converter: MetaConverter<T> = converter
override suspend fun read(device: D): T =
withContext(device.coroutineContext) { readWriteProperty.get(device) }
override suspend fun write(device: D, value: T) = withContext(device.coroutineContext) {
readWriteProperty.set(device, value)
}
}
registerProperty(deviceProperty)
return deviceProperty
}
public fun <T : Any> property(
converter: MetaConverter<T>,
@ -30,15 +75,14 @@ public abstract class DeviceSpec<D : DeviceBySpec<D>>(
override val descriptor: PropertyDescriptor = PropertyDescriptor(this.name).apply(descriptorBuilder)
override val converter: MetaConverter<T> = converter
override suspend fun read(device: D): T = device.read()
override suspend fun read(device: D): T = withContext(device.coroutineContext) { device.read() }
}
deviceProperties[propertyName] = deviceProperty
_properties[propertyName] = deviceProperty
ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, T>> { _, _ ->
deviceProperty
}
}
public fun <T : Any> property(
converter: MetaConverter<T>,
name: String? = null,
@ -53,19 +97,24 @@ public abstract class DeviceSpec<D : DeviceBySpec<D>>(
override val descriptor: PropertyDescriptor = PropertyDescriptor(this.name).apply(descriptorBuilder)
override val converter: MetaConverter<T> = converter
override suspend fun read(device: D): T = device.read()
override suspend fun read(device: D): T = withContext(device.coroutineContext) { device.read() }
override suspend fun write(device: D, value: T) {
override suspend fun write(device: D, value: T) = withContext(device.coroutineContext) {
device.write(value)
}
}
deviceProperties[propertyName] = deviceProperty
_properties[propertyName] = deviceProperty
ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, T>> { _, _ ->
deviceProperty
}
}
public fun <I : Any, O : Any> registerAction(deviceAction: DeviceActionSpec<D, I, O>): DeviceActionSpec<D, I, O> {
_actions[deviceAction.name] = deviceAction
return deviceAction
}
public fun <I : Any, O : Any> action(
inputConverter: MetaConverter<I>,
outputConverter: MetaConverter<O>,
@ -82,21 +131,30 @@ public abstract class DeviceSpec<D : DeviceBySpec<D>>(
override val inputConverter: MetaConverter<I> = inputConverter
override val outputConverter: MetaConverter<O> = outputConverter
override suspend fun execute(device: D, input: I?): O? {
return device.execute(input)
override suspend fun execute(device: D, input: I?): O? = withContext(device.coroutineContext) {
device.execute(input)
}
}
deviceActions[actionName] = deviceAction
_actions[actionName] = deviceAction
ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, I, O>> { _, _ ->
deviceAction
}
}
/**
* The function is executed right after device initialization is finished
*/
public open fun D.onStartup(){}
/**
* The function is executed before device is shut down
*/
public open fun D.onShutdown(){}
override fun invoke(meta: Meta, context: Context): D = buildDevice().apply {
this.context = context
this.meta = meta
this.properties = deviceProperties
this.actions = deviceActions
onStartup()
}
}

View File

@ -0,0 +1,12 @@
package ru.mipt.npm.controls.properties
import space.kscience.dataforge.meta.transformations.MetaConverter
import kotlin.properties.ReadWriteProperty
public fun <D : DeviceBySpec<D>> D.state(
initialValue: Double,
): ReadWriteProperty<D, Double> = state(MetaConverter.double, initialValue)
public fun <D : DeviceBySpec<D>> D.state(
initialValue: Number,
): ReadWriteProperty<D, Number> = state(MetaConverter.number, initialValue)

View File

@ -0,0 +1,105 @@
package ru.mipt.npm.controls.properties
import ru.mipt.npm.controls.api.PropertyDescriptor
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.MetaItem
import space.kscience.dataforge.meta.TypedMetaItem
import space.kscience.dataforge.meta.transformations.MetaConverter
import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadOnlyProperty
//read only delegates
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.booleanProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> Boolean
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Boolean>>> =
property(MetaConverter.boolean, name, descriptorBuilder, read)
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.numberProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> Number
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Number>>> =
property(MetaConverter.number, name, descriptorBuilder, read)
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.doubleProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> Double
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Double>>> =
property(MetaConverter.double, name, descriptorBuilder, read)
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.stringProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> String
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, String>>> =
property(MetaConverter.string, name, descriptorBuilder, read)
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.itemProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> MetaItem
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, MetaItem>>> =
property(MetaConverter.item, name, descriptorBuilder, read)
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.metaProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> Meta
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DevicePropertySpec<D, Meta>>> =
property(MetaConverter.meta, name, descriptorBuilder, read)
//read-write delegates
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.booleanProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> Boolean,
write: suspend D.(Boolean) -> Unit
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, Boolean>>> =
property(MetaConverter.boolean, name, descriptorBuilder, read, write)
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.numberProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> Number,
write: suspend D.(Number) -> Unit
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, Number>>> =
property(MetaConverter.number, name, descriptorBuilder, read, write)
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.doubleProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> Double,
write: suspend D.(Double) -> Unit
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, Double>>> =
property(MetaConverter.double, name, descriptorBuilder, read, write)
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.stringProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> String,
write: suspend D.(String) -> Unit
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, String>>> =
property(MetaConverter.string, name, descriptorBuilder, read, write)
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.itemProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> MetaItem,
write: suspend D.(MetaItem) -> Unit
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, TypedMetaItem<*>>>> =
property(MetaConverter.item, name, descriptorBuilder, read, write)
public fun <D : DeviceBySpec<D>> DeviceSpec<D>.metaProperty(
name: String? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
read: suspend D.() -> Meta,
write: suspend D.(Meta) -> Unit
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, WritableDevicePropertySpec<D, Meta>>> =
property(MetaConverter.meta, name, descriptorBuilder, read, write)

View File

@ -0,0 +1,13 @@
package ru.mipt.npm.controls.properties
import kotlinx.coroutines.runBlocking
import ru.mipt.npm.controls.api.PropertyDescriptor
import space.kscience.dataforge.meta.transformations.MetaConverter
import kotlin.reflect.KFunction
/**
* Blocking property get call
*/
public operator fun <D : DeviceBySpec<D>, T : Any> D.get(
propertySpec: DevicePropertySpec<D, T>
): T = runBlocking { getAsync(propertySpec).await() }

View File

@ -95,9 +95,9 @@ class DemoControllerView : View(title = " Demo controller remote") {
useMaxWidth = true
action {
controller.device?.apply {
timeScaleValue = timeScaleSlider.value
sinScaleValue = xScaleSlider.value
cosScaleValue = yScaleSlider.value
timeScale = timeScaleSlider.value
sinScale = xScaleSlider.value
cosScale = yScaleSlider.value
}
}
}

View File

@ -1,73 +1,59 @@
package ru.mipt.npm.controls.demo
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.asCoroutineDispatcher
import ru.mipt.npm.controls.base.*
import ru.mipt.npm.controls.controllers.DeviceSpec
import ru.mipt.npm.controls.controllers.double
import space.kscience.dataforge.context.Context
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import ru.mipt.npm.controls.properties.*
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.values.asValue
import space.kscience.dataforge.meta.transformations.MetaConverter
import java.time.Instant
import java.util.concurrent.Executors
import kotlin.math.cos
import kotlin.math.sin
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
@OptIn(ExperimentalTime::class)
class DemoDevice(context: Context) : DeviceBase(context) {
private val executor = Executors.newSingleThreadExecutor()
class DemoDevice : DeviceBySpec<DemoDevice>(DemoDevice) {
var timeScale by state(5000.0)
var sinScale by state( 1.0)
var cosScale by state(1.0)
override val scope: CoroutineScope = CoroutineScope(
context.coroutineContext + executor.asCoroutineDispatcher() + Job(context.coroutineContext[Job])
)
companion object : DeviceSpec<DemoDevice>(::DemoDevice) {
// register virtual properties based on actual object state
val timeScaleProperty = registerProperty(MetaConverter.double, DemoDevice::timeScale)
val sinScaleProperty = registerProperty(MetaConverter.double, DemoDevice::sinScale)
val cosScaleProperty = registerProperty(MetaConverter.double, DemoDevice::cosScale)
val timeScale: DeviceProperty by writingVirtual(5000.0.asValue())
var timeScaleValue by timeScale.double()
val sinScale by writingVirtual(1.0.asValue())
var sinScaleValue by sinScale.double()
val sin: TypedReadOnlyDeviceProperty<Number> by readingNumber {
val sin by doubleProperty {
val time = Instant.now()
sin(time.toEpochMilli().toDouble() / timeScaleValue) * sinScaleValue
kotlin.math.sin(time.toEpochMilli().toDouble() / timeScale) * sinScale
}
val cosScale by writingVirtual(1.0.asValue())
var cosScaleValue by cosScale.double()
val cos by readingNumber {
val cos by doubleProperty {
val time = Instant.now()
cos(time.toEpochMilli().toDouble() / timeScaleValue) * cosScaleValue
kotlin.math.cos(time.toEpochMilli().toDouble() / timeScale) * sinScale
}
val coordinates by readingMeta {
val coordinates by metaProperty {
Meta {
val time = Instant.now()
"time" put time.toEpochMilli()
"x" put sin(time.toEpochMilli().toDouble() / timeScaleValue) * sinScaleValue
"y" put cos(time.toEpochMilli().toDouble() / timeScaleValue) * cosScaleValue
"x" put getSuspend(sin)
"y" put getSuspend(cos)
}
}
val resetScale: DeviceAction by acting {
timeScaleValue = 5000.0
sinScaleValue = 1.0
cosScaleValue = 1.0
val resetScale by action(MetaConverter.meta, MetaConverter.meta) {
timeScale = 5000.0
sinScale = 1.0
cosScale = 1.0
null
}
init {
sin.readEvery(Duration.seconds(0.2))
cos.readEvery(Duration.seconds(0.2))
coordinates.readEvery(Duration.seconds(0.3))
override fun DemoDevice.onStartup() {
launch {
while(isActive){
delay(50)
sin.read()
cos.read()
}
}
override fun close() {
super.close()
executor.shutdown()
}
companion object : DeviceSpec<DemoDevice> {
override fun invoke(meta: Meta, context: Context): DemoDevice = DemoDevice(context)
}
}

View File

@ -11,7 +11,6 @@ import kotlinx.coroutines.sync.withLock
import ru.mipt.npm.controls.api.DeviceHub
import ru.mipt.npm.controls.api.PropertyDescriptor
import ru.mipt.npm.controls.base.*
import ru.mipt.npm.controls.controllers.DeviceSpec
import ru.mipt.npm.controls.controllers.duration
import ru.mipt.npm.controls.ports.*
import space.kscience.dataforge.context.*
@ -28,10 +27,6 @@ class PiMotionMasterDevice(
private val portFactory: PortFactory = KtorTcpPort,
) : DeviceBase(context), DeviceHub {
override val scope: CoroutineScope = CoroutineScope(
context.coroutineContext + SupervisorJob(context.coroutineContext[Job])
)
private var port: Port? = null
//TODO make proxy work
//PortProxy { portFactory(address ?: error("The device is not connected"), context) }
@ -151,11 +146,10 @@ class PiMotionMasterDevice(
withTimeout(timeoutValue) {
sendCommandInternal(command, *arguments)
val phrases = port?.receiving()?.withDelimiter("\n") ?: error("Not connected to device")
val list = phrases.transformWhile { line ->
phrases.transformWhile { line ->
emit(line)
line.endsWith(" \n")
}.toList()
list
}
} catch (ex: Throwable) {
logger.warn { "Error during PIMotionMaster request. Requesting error code." }
@ -204,7 +198,6 @@ class PiMotionMasterDevice(
)
inner class Axis(val axisId: String) : DeviceBase(context) {
override val scope: CoroutineScope get() = this@PiMotionMasterDevice.scope
private suspend fun readAxisBoolean(command: String): Boolean =
requestAndParse(command, axisId)[axisId]?.toIntOrNull()
@ -343,7 +336,7 @@ class PiMotionMasterDevice(
}
}
companion object : DeviceSpec<PiMotionMasterDevice> {
companion object : Factory<PiMotionMasterDevice> {
override fun invoke(meta: Meta, context: Context): PiMotionMasterDevice = PiMotionMasterDevice(context)
}