Cleanup DeviceWithSpec API. Now all public things are in Spec
This commit is contained in:
parent
d503f0499e
commit
3aa00ec491
@ -47,7 +47,7 @@ public interface Device : Closeable, ContextAware, CoroutineScope {
|
||||
* 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 writeItem(propertyName: String, value: Meta)
|
||||
public suspend fun writeProperty(propertyName: String, value: Meta)
|
||||
|
||||
/**
|
||||
* A subscription-based [Flow] of [DeviceMessage] provided by device. The flow is guaranteed to be readable
|
||||
|
@ -59,8 +59,8 @@ public operator fun DeviceHub.get(nameString: String): Device =
|
||||
public suspend fun DeviceHub.readProperty(deviceName: Name, propertyName: String): Meta =
|
||||
this[deviceName].readProperty(propertyName)
|
||||
|
||||
public suspend fun DeviceHub.writeItem(deviceName: Name, propertyName: String, value: Meta) {
|
||||
this[deviceName].writeItem(propertyName, value)
|
||||
public suspend fun DeviceHub.writeProperty(deviceName: Name, propertyName: String, value: Meta) {
|
||||
this[deviceName].writeProperty(propertyName, value)
|
||||
}
|
||||
|
||||
public suspend fun DeviceHub.execute(deviceName: Name, command: String, argument: Meta?): Meta? =
|
||||
|
@ -164,7 +164,7 @@ public abstract class DeviceBase(final override val context: Context) : Device {
|
||||
(_properties[propertyName] ?: error("Property with name $propertyName not defined")).invalidate()
|
||||
}
|
||||
|
||||
override suspend fun writeItem(propertyName: String, value: Meta) {
|
||||
override suspend fun writeProperty(propertyName: String, value: Meta) {
|
||||
(_properties[propertyName] as? DeviceProperty ?: error("Property with name $propertyName not defined")).write(
|
||||
value
|
||||
)
|
||||
|
@ -25,7 +25,7 @@ public suspend fun Device.respondMessage(deviceTarget: Name, request: DeviceMess
|
||||
if (request.value == null) {
|
||||
invalidate(request.property)
|
||||
} else {
|
||||
writeItem(request.property, request.value)
|
||||
writeProperty(request.property, request.value)
|
||||
}
|
||||
PropertyChangedMessage(
|
||||
property = request.property,
|
||||
|
@ -11,11 +11,7 @@ import ru.mipt.npm.controls.api.*
|
||||
import space.kscience.dataforge.context.Context
|
||||
import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.properties.Delegates.observable
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
/**
|
||||
* A device generated from specification
|
||||
@ -58,7 +54,7 @@ public open class DeviceBySpec<D : DeviceBySpec<D>>(
|
||||
|
||||
private val stateLock = Mutex()
|
||||
|
||||
private suspend fun updateLogical(propertyName: String, value: Meta?) {
|
||||
protected suspend fun updateLogical(propertyName: String, value: Meta?) {
|
||||
if (value != logicalState[propertyName]) {
|
||||
stateLock.withLock {
|
||||
logicalState[propertyName] = value
|
||||
@ -74,7 +70,7 @@ public open class DeviceBySpec<D : DeviceBySpec<D>>(
|
||||
* The logical state is updated after read
|
||||
*/
|
||||
override suspend fun readProperty(propertyName: String): Meta {
|
||||
val newValue = properties[propertyName]?.readItem(self)
|
||||
val newValue = properties[propertyName]?.readMeta(self)
|
||||
?: error("A property with name $propertyName is not registered in $this")
|
||||
updateLogical(propertyName, newValue)
|
||||
return newValue
|
||||
@ -88,10 +84,10 @@ public open class DeviceBySpec<D : DeviceBySpec<D>>(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun writeItem(propertyName: String, value: Meta): Unit {
|
||||
override suspend fun writeProperty(propertyName: String, value: Meta): 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)
|
||||
(properties[propertyName] as? WritableDevicePropertySpec<D, out Any?>)?.let {
|
||||
it.writeMeta(self, value)
|
||||
invalidate(propertyName)
|
||||
} ?: run {
|
||||
updateLogical(propertyName, value)
|
||||
@ -99,39 +95,23 @@ public open class DeviceBySpec<D : DeviceBySpec<D>>(
|
||||
}
|
||||
|
||||
override suspend fun execute(action: String, argument: Meta?): Meta? =
|
||||
actions[action]?.executeItem(self, argument)
|
||||
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
invalidate(property.name)
|
||||
sharedMessageFlow.emit(PropertyChangedMessage(property.name, converter.objectToMeta(newValue)))
|
||||
}
|
||||
}
|
||||
}
|
||||
actions[action]?.executeMeta(self, argument)
|
||||
|
||||
/**
|
||||
* Read typed value and update/push event if needed
|
||||
*/
|
||||
public suspend fun <T : Any> DevicePropertySpec<D, T>.read(): T {
|
||||
public suspend fun <T> DevicePropertySpec<D, T>.read(): T {
|
||||
val res = read(self)
|
||||
updateLogical(name, converter.objectToMeta(res))
|
||||
return res
|
||||
}
|
||||
|
||||
public fun <T : Any> DevicePropertySpec<D, T>.get(): T? = getProperty(name)?.let(converter::metaToObject)
|
||||
public fun <T> DevicePropertySpec<D, T>.get(): T? = getProperty(name)?.let(converter::metaToObject)
|
||||
|
||||
/**
|
||||
* Write typed property state and invalidate logical state
|
||||
*/
|
||||
public suspend fun <T : Any> WritableDevicePropertySpec<D, T>.write(value: T) {
|
||||
public suspend fun <T> WritableDevicePropertySpec<D, T>.write(value: T) {
|
||||
write(self, value)
|
||||
invalidate(name)
|
||||
}
|
||||
@ -146,7 +126,7 @@ public suspend fun <D : DeviceBySpec<D>, T : Any> D.read(
|
||||
propertySpec: DevicePropertySpec<D, T>
|
||||
): T = propertySpec.read()
|
||||
|
||||
public fun <D : DeviceBySpec<D>, T : Any> D.write(
|
||||
public fun <D : DeviceBySpec<D>, T> D.write(
|
||||
propertySpec: WritableDevicePropertySpec<D, T>,
|
||||
value: T
|
||||
): Job = launch {
|
||||
|
@ -5,8 +5,6 @@ import ru.mipt.npm.controls.api.Device
|
||||
import ru.mipt.npm.controls.api.PropertyDescriptor
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import space.kscience.dataforge.meta.transformations.nullableMetaToObject
|
||||
import space.kscience.dataforge.meta.transformations.nullableObjectToMeta
|
||||
|
||||
|
||||
/**
|
||||
@ -15,8 +13,7 @@ import space.kscience.dataforge.meta.transformations.nullableObjectToMeta
|
||||
@RequiresOptIn
|
||||
public annotation class InternalDeviceAPI
|
||||
|
||||
//TODO relax T restriction after DF 0.4.4
|
||||
public interface DevicePropertySpec<in D : Device, T : Any> {
|
||||
public interface DevicePropertySpec<in D : Device, T> {
|
||||
/**
|
||||
* Property name, should be unique in device
|
||||
*/
|
||||
@ -40,11 +37,11 @@ public interface DevicePropertySpec<in D : Device, T : Any> {
|
||||
}
|
||||
|
||||
@OptIn(InternalDeviceAPI::class)
|
||||
public suspend fun <D : Device, T : Any> DevicePropertySpec<D, T>.readItem(device: D): Meta =
|
||||
public suspend fun <D : Device, T> DevicePropertySpec<D, T>.readMeta(device: D): Meta =
|
||||
converter.objectToMeta(read(device))
|
||||
|
||||
|
||||
public interface WritableDevicePropertySpec<in D : Device, T : Any> : DevicePropertySpec<D, T> {
|
||||
public interface WritableDevicePropertySpec<in D : Device, T> : DevicePropertySpec<D, T> {
|
||||
/**
|
||||
* Write physical value to a device
|
||||
*/
|
||||
@ -53,11 +50,11 @@ public interface WritableDevicePropertySpec<in D : Device, T : Any> : DeviceProp
|
||||
}
|
||||
|
||||
@OptIn(InternalDeviceAPI::class)
|
||||
public suspend fun <D : Device, T : Any> WritableDevicePropertySpec<D, T>.writeItem(device: D, item: Meta) {
|
||||
public suspend fun <D : Device, T> WritableDevicePropertySpec<D, T>.writeMeta(device: D, item: Meta) {
|
||||
write(device, converter.metaToObject(item) ?: error("Meta $item could not be read with $converter"))
|
||||
}
|
||||
|
||||
public interface DeviceActionSpec<in D : Device, I : Any, O : Any> {
|
||||
public interface DeviceActionSpec<in D : Device, I, O> {
|
||||
/**
|
||||
* Action name, should be unique in device
|
||||
*/
|
||||
@ -78,11 +75,11 @@ public interface DeviceActionSpec<in D : Device, I : Any, O : Any> {
|
||||
public suspend fun execute(device: D, input: I?): O?
|
||||
}
|
||||
|
||||
public suspend fun <D : Device, I : Any, O : Any> DeviceActionSpec<D, I, O>.executeItem(
|
||||
public suspend fun <D : Device, I, O> DeviceActionSpec<D, I, O>.executeMeta(
|
||||
device: D,
|
||||
item: Meta?
|
||||
): Meta? {
|
||||
val arg = inputConverter.nullableMetaToObject(item)
|
||||
val arg = item?.let { inputConverter.metaToObject(item) }
|
||||
val res = execute(device, arg)
|
||||
return outputConverter.nullableObjectToMeta(res)
|
||||
return res?.let { outputConverter.objectToMeta(res) }
|
||||
}
|
@ -52,8 +52,9 @@ public abstract class DeviceSpec<D : DeviceBySpec<D>>(
|
||||
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 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)
|
||||
@ -145,12 +146,12 @@ public abstract class DeviceSpec<D : DeviceBySpec<D>>(
|
||||
/**
|
||||
* The function is executed right after device initialization is finished
|
||||
*/
|
||||
public open fun D.onStartup(){}
|
||||
public open fun D.onStartup() {}
|
||||
|
||||
/**
|
||||
* The function is executed before device is shut down
|
||||
*/
|
||||
public open fun D.onShutdown(){}
|
||||
public open fun D.onShutdown() {}
|
||||
|
||||
|
||||
override fun invoke(meta: Meta, context: Context): D = buildDevice().apply {
|
||||
|
@ -1,12 +0,0 @@
|
||||
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)
|
@ -94,7 +94,7 @@ public fun DeviceManager.launchTangoMagix(
|
||||
}
|
||||
TangoAction.write -> {
|
||||
request.payload.value?.let { value ->
|
||||
device.writeItem(request.payload.name, value)
|
||||
device.writeProperty(request.payload.name, value)
|
||||
}
|
||||
//wait for value to be written and return final state
|
||||
val value = device.getOrReadItem(request.payload.name)
|
||||
|
@ -8,6 +8,7 @@ val miloVersion: String = "0.6.3"
|
||||
|
||||
dependencies {
|
||||
api(project(":controls-core"))
|
||||
api("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:${ru.mipt.npm.gradle.KScienceVersions.coroutinesVersion}")
|
||||
implementation("org.eclipse.milo:sdk-client:$miloVersion")
|
||||
implementation("org.eclipse.milo:bsd-parser:$miloVersion")
|
||||
implementation("org.eclipse.milo:dictionary-reader:$miloVersion")
|
||||
|
@ -1,16 +1,12 @@
|
||||
package ru.mipt.npm.controls.opcua
|
||||
|
||||
import kotlinx.coroutines.future.await
|
||||
import org.eclipse.milo.opcua.sdk.client.OpcUaClient
|
||||
import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue
|
||||
import org.eclipse.milo.opcua.stack.core.types.builtin.ExtensionObject
|
||||
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId
|
||||
import org.eclipse.milo.opcua.stack.core.types.builtin.Variant
|
||||
import org.eclipse.milo.opcua.stack.core.types.builtin.*
|
||||
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn
|
||||
import ru.mipt.npm.controls.api.Device
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
|
||||
/**
|
||||
@ -28,40 +24,46 @@ public interface MiloDevice : Device {
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun <reified T> MiloDevice.opc(
|
||||
public suspend inline fun <reified T> MiloDevice.readOpcWithTime(
|
||||
nodeId: NodeId,
|
||||
converter: MetaConverter<T>,
|
||||
magAge: Double = 500.0
|
||||
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
|
||||
val data = client.readValue(magAge, TimestampsToReturn.Server, nodeId).get()
|
||||
val meta: Meta = when (val content = data.value.value) {
|
||||
is T -> return content
|
||||
content is Meta -> content as Meta
|
||||
content is ExtensionObject -> (content as ExtensionObject).decode(client.dynamicSerializationContext) as Meta
|
||||
else -> error("Incompatible OPC property value $content")
|
||||
}
|
||||
|
||||
return converter.metaToObject(meta) ?: error("Meta $meta could not be converted to ${T::class}")
|
||||
): Pair<T, DateTime> {
|
||||
val data = client.readValue(magAge, TimestampsToReturn.Server, nodeId).await()
|
||||
val time = data.serverTime ?: error("No server time provided")
|
||||
val meta: Meta = when (val content = data.value.value) {
|
||||
is T -> return content to time
|
||||
content is Meta -> content as Meta
|
||||
content is ExtensionObject -> (content as ExtensionObject).decode(client.dynamicSerializationContext) as Meta
|
||||
else -> error("Incompatible OPC property value $content")
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
val meta = converter.objectToMeta(value)
|
||||
client.writeValue(nodeId, DataValue(Variant(meta)))
|
||||
}
|
||||
val res = converter.metaToObject(meta) ?: error("Meta $meta could not be converted to ${T::class}")
|
||||
return res to time
|
||||
}
|
||||
|
||||
public inline fun <reified T> MiloDevice.opcDouble(
|
||||
public suspend inline fun <reified T> MiloDevice.readOpc(
|
||||
nodeId: NodeId,
|
||||
magAge: Double = 1.0
|
||||
): ReadWriteProperty<Any?, Double> = opc(nodeId, MetaConverter.double, magAge)
|
||||
converter: MetaConverter<T>,
|
||||
magAge: Double = 500.0
|
||||
): T {
|
||||
val data = client.readValue(magAge, TimestampsToReturn.Neither, nodeId).await()
|
||||
|
||||
public inline fun <reified T> MiloDevice.opcInt(
|
||||
nodeId: NodeId,
|
||||
magAge: Double = 1.0
|
||||
): ReadWriteProperty<Any?, Int> = opc(nodeId, MetaConverter.int, magAge)
|
||||
val meta: Meta = when (val content = data.value.value) {
|
||||
is T -> return content
|
||||
content is Meta -> content as Meta
|
||||
content is ExtensionObject -> (content as ExtensionObject).decode(client.dynamicSerializationContext) as Meta
|
||||
else -> error("Incompatible OPC property value $content")
|
||||
}
|
||||
|
||||
public inline fun <reified T> MiloDevice.opcString(
|
||||
return converter.metaToObject(meta) ?: error("Meta $meta could not be converted to ${T::class}")
|
||||
}
|
||||
|
||||
public suspend inline fun <reified T> MiloDevice.writeOpc(
|
||||
nodeId: NodeId,
|
||||
magAge: Double = 1.0
|
||||
): ReadWriteProperty<Any?, String> = opc(nodeId, MetaConverter.string, magAge)
|
||||
converter: MetaConverter<T>,
|
||||
value: T
|
||||
): StatusCode {
|
||||
val meta = converter.objectToMeta(value)
|
||||
return client.writeValue(nodeId, DataValue(Variant(meta))).await()
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
package ru.mipt.npm.controls.opcua
|
||||
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.eclipse.milo.opcua.sdk.client.OpcUaClient
|
||||
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId
|
||||
import ru.mipt.npm.controls.properties.DeviceBySpec
|
||||
import ru.mipt.npm.controls.properties.DeviceSpec
|
||||
import space.kscience.dataforge.context.Context
|
||||
@ -8,12 +11,15 @@ import space.kscience.dataforge.context.Global
|
||||
import space.kscience.dataforge.meta.Meta
|
||||
import space.kscience.dataforge.meta.get
|
||||
import space.kscience.dataforge.meta.string
|
||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||
import kotlin.properties.ReadWriteProperty
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
public open class MiloDeviceBySpec<D: MiloDeviceBySpec<D>>(
|
||||
public open class MiloDeviceBySpec<D : MiloDeviceBySpec<D>>(
|
||||
spec: DeviceSpec<D>,
|
||||
context: Context = Global,
|
||||
meta: Meta = Meta.EMPTY
|
||||
): MiloDevice, DeviceBySpec<D>(spec, context, meta) {
|
||||
) : MiloDevice, DeviceBySpec<D>(spec, context, meta) {
|
||||
|
||||
override val client: OpcUaClient by lazy {
|
||||
val endpointUrl = meta["endpointUrl"].string ?: error("Endpoint url is not defined")
|
||||
@ -27,3 +33,37 @@ public open class MiloDeviceBySpec<D: MiloDeviceBySpec<D>>(
|
||||
super<DeviceBySpec>.close()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A device-bound OPC-UA property. Does not trigger device properties change.
|
||||
*/
|
||||
public inline fun <reified T> MiloDeviceBySpec<*>.opc(
|
||||
nodeId: NodeId,
|
||||
converter: MetaConverter<T>,
|
||||
magAge: Double = 500.0
|
||||
): ReadWriteProperty<Any?, T> = object : ReadWriteProperty<Any?, T> {
|
||||
override fun getValue(thisRef: Any?, property: KProperty<*>): T = runBlocking {
|
||||
readOpc(nodeId, converter, magAge)
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||
launch {
|
||||
writeOpc(nodeId, converter, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public inline fun <reified T> MiloDeviceBySpec<*>.opcDouble(
|
||||
nodeId: NodeId,
|
||||
magAge: Double = 1.0
|
||||
): ReadWriteProperty<Any?, Double> = opc(nodeId, MetaConverter.double, magAge)
|
||||
|
||||
public inline fun <reified T> MiloDeviceBySpec<*>.opcInt(
|
||||
nodeId: NodeId,
|
||||
magAge: Double = 1.0
|
||||
): ReadWriteProperty<Any?, Int> = opc(nodeId, MetaConverter.int, magAge)
|
||||
|
||||
public inline fun <reified T> MiloDeviceBySpec<*>.opcString(
|
||||
nodeId: NodeId,
|
||||
magAge: Double = 1.0
|
||||
): ReadWriteProperty<Any?, String> = opc(nodeId, MetaConverter.string, magAge)
|
@ -10,6 +10,9 @@ import ru.mipt.npm.controls.api.DeviceMessage
|
||||
import ru.mipt.npm.controls.client.connectToMagix
|
||||
import ru.mipt.npm.controls.controllers.DeviceManager
|
||||
import ru.mipt.npm.controls.controllers.install
|
||||
import ru.mipt.npm.controls.demo.DemoDevice.Companion.cosScale
|
||||
import ru.mipt.npm.controls.demo.DemoDevice.Companion.sinScale
|
||||
import ru.mipt.npm.controls.demo.DemoDevice.Companion.timeScale
|
||||
import ru.mipt.npm.magix.api.MagixEndpoint
|
||||
import ru.mipt.npm.magix.rsocket.rSocketWithTcp
|
||||
import ru.mipt.npm.magix.rsocket.rSocketWithWebSockets
|
||||
@ -97,10 +100,12 @@ class DemoControllerView : View(title = " Demo controller remote") {
|
||||
button("Submit") {
|
||||
useMaxWidth = true
|
||||
action {
|
||||
controller.device?.apply {
|
||||
timeScale = timeScaleSlider.value
|
||||
sinScale = xScaleSlider.value
|
||||
cosScale = yScaleSlider.value
|
||||
controller.device?.run {
|
||||
launch {
|
||||
timeScale.write(timeScaleSlider.value)
|
||||
sinScale.write(xScaleSlider.value)
|
||||
cosScale.write(yScaleSlider.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,24 +9,24 @@ import kotlin.time.ExperimentalTime
|
||||
|
||||
|
||||
class DemoDevice : DeviceBySpec<DemoDevice>(DemoDevice) {
|
||||
var timeScale by state(5000.0)
|
||||
var sinScale by state(1.0)
|
||||
var cosScale by state(1.0)
|
||||
private var timeScaleState = 5000.0
|
||||
private var sinScaleState = 1.0
|
||||
private var cosScaleState = 1.0
|
||||
|
||||
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 = registerProperty(MetaConverter.double, DemoDevice::timeScaleState)
|
||||
val sinScale = registerProperty(MetaConverter.double, DemoDevice::sinScaleState)
|
||||
val cosScale = registerProperty(MetaConverter.double, DemoDevice::cosScaleState)
|
||||
|
||||
val sin by doubleProperty {
|
||||
val time = Instant.now()
|
||||
kotlin.math.sin(time.toEpochMilli().toDouble() / timeScale) * sinScale
|
||||
kotlin.math.sin(time.toEpochMilli().toDouble() / timeScaleState) * sinScaleState
|
||||
}
|
||||
|
||||
val cos by doubleProperty {
|
||||
val time = Instant.now()
|
||||
kotlin.math.cos(time.toEpochMilli().toDouble() / timeScale) * sinScale
|
||||
kotlin.math.cos(time.toEpochMilli().toDouble() / timeScaleState) * sinScaleState
|
||||
}
|
||||
|
||||
val coordinates by metaProperty {
|
||||
@ -39,9 +39,9 @@ class DemoDevice : DeviceBySpec<DemoDevice>(DemoDevice) {
|
||||
}
|
||||
|
||||
val resetScale by action(MetaConverter.meta, MetaConverter.meta) {
|
||||
timeScale = 5000.0
|
||||
sinScale = 1.0
|
||||
cosScale = 1.0
|
||||
timeScale.write(5000.0)
|
||||
sinScale.write(1.0)
|
||||
cosScale.write(1.0)
|
||||
null
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user