diff --git a/.space.kts b/.space.kts index fe28b1f..c5dd962 100644 --- a/.space.kts +++ b/.space.kts @@ -1,3 +1,45 @@ -job("Build and run tests") { - gradle("gradle:8.1.1-jdk11", "build") +import kotlin.io.path.readText + +job("Build") { + gradlew("spc.registry.jetbrains.space/p/sci/containers/kotlin-ci:1.0.3", "build") +} + +job("Publish") { + startOn { + gitPush { enabled = false } + } + container("spc.registry.jetbrains.space/p/sci/containers/kotlin-ci:1.0.3") { + env["SPACE_USER"] = "{{ project:space_user }}" + env["SPACE_TOKEN"] = "{{ project:space_token }}" + kotlinScript { api -> + + val spaceUser = System.getenv("SPACE_USER") + val spaceToken = System.getenv("SPACE_TOKEN") + + // write the version to the build directory + api.gradlew("version") + + //read the version from build file + val version = java.nio.file.Path.of("build/project-version.txt").readText() + + val revisionSuffix = if (version.endsWith("SNAPSHOT")) { + "-" + api.gitRevision().take(7) + } else { + "" + } + + api.space().projects.automation.deployments.start( + project = api.projectIdentifier(), + targetIdentifier = TargetIdentifier.Key("maps-kt"), + version = version+revisionSuffix, + // automatically update deployment status based on the status of a job + syncWithAutomationJob = true + ) + api.gradlew( + "publishAllPublicationsToSpaceRepository", + "-Ppublishing.space.user=\"$spaceUser\"", + "-Ppublishing.space.token=\"$spaceToken\"", + ) + } + } } \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 1e3ae71..7327460 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,8 +35,4 @@ ksciencePublish { space("https://maven.pkg.jetbrains.space/spc/p/controls/maven") } -readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md") - -apiValidation { - validationDisabled = true -} \ No newline at end of file +readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md") \ No newline at end of file diff --git a/controls-core/build.gradle.kts b/controls-core/build.gradle.kts index b66fc12..41acc60 100644 --- a/controls-core/build.gradle.kts +++ b/controls-core/build.gradle.kts @@ -13,6 +13,7 @@ kscience { useSerialization{ json() } + useContextReceivers() dependencies { api("space.kscience:dataforge-io:$dataforgeVersion") api(spclibs.kotlinx.datetime) diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt index 4546975..e74ca2d 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBase.kt @@ -4,7 +4,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob 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 space.kscience.controls.api.* @@ -18,7 +17,7 @@ import kotlin.coroutines.CoroutineContext * A base abstractions for [Device], introducing specifications for properties */ @OptIn(InternalDeviceAPI::class) -public abstract class DeviceBase>( +public abstract class DeviceBase( override val context: Context = Global, override val meta: Meta = Meta.EMPTY, ) : Device { @@ -75,7 +74,7 @@ public abstract class DeviceBase>( /** * Update logical state using given [spec] and its convertor */ - protected suspend fun updateLogical(spec: DevicePropertySpec, value: T) { + public suspend fun updateLogical(spec: DevicePropertySpec, value: T) { updateLogical(spec.name, spec.converter.objectToMeta(value)) } @@ -99,7 +98,7 @@ public abstract class DeviceBase>( } 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 + //If there is a physical property with a given name, invalidate logical property and write physical one (properties[propertyName] as? WritableDevicePropertySpec)?.let { invalidate(propertyName) it.writeMeta(self, value) @@ -111,49 +110,13 @@ public abstract class DeviceBase>( override suspend fun execute(action: String, argument: Meta?): Meta? = actions[action]?.executeWithMeta(self, argument) - /** - * Read typed value and update/push event if needed. - * Return null if property read is not successful or property is undefined. - */ - public suspend fun DevicePropertySpec.readOrNull(): T? { - val res = read(self) ?: return null - updateLogical(name, converter.objectToMeta(res)) - return res - } - - public suspend fun DevicePropertySpec.read(): T = - readOrNull() ?: error("Failed to read property $name state") - - public fun DevicePropertySpec.get(): T? = getProperty(name)?.let(converter::metaToObject) - - /** - * Write typed property state and invalidate logical state - */ - public suspend fun WritableDevicePropertySpec.write(value: T) { - invalidate(name) - write(self, value) - //perform asynchronous read and update after write - launch { - read() - } - } - - /** - * Reset logical state of a property - */ - public suspend fun DevicePropertySpec.invalidate() { - invalidate(name) - } - - public suspend operator fun DeviceActionSpec.invoke(input: I? = null): O? = execute(self, input) - } /** * A device generated from specification * @param D recursive self-type for properties and actions */ -public open class DeviceBySpec>( +public open class DeviceBySpec( public val spec: DeviceSpec, context: Context = Global, meta: Meta = Meta.EMPTY, diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DevicePropertySpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DevicePropertySpec.kt index 4fff906..53b3953 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DevicePropertySpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DevicePropertySpec.kt @@ -91,30 +91,56 @@ public suspend fun DeviceActionSpec.executeWithMeta( return res?.let { outputConverter.objectToMeta(res) } } - -public suspend fun , T : Any> D.read( - propertySpec: DevicePropertySpec, -): T = propertySpec.read() - -public suspend fun D.read( - propertySpec: DevicePropertySpec, -): T = propertySpec.converter.metaToObject(readProperty(propertySpec.name)) - ?: error("Property meta converter returned null") - -public fun D.write( - propertySpec: WritableDevicePropertySpec, - value: T, -): Job = launch { - writeProperty(propertySpec.name, propertySpec.converter.objectToMeta(value)) +/** + * Read typed value and update/push event if needed. + * Return null if property read is not successful or property is undefined. + */ +@OptIn(InternalDeviceAPI::class) +public suspend fun D.readOrNull(propertySpec: DevicePropertySpec): T? { + val res = propertySpec.read(this) ?: return null + @Suppress("UNCHECKED_CAST") + (this as? DeviceBase)?.updateLogical(propertySpec, res) + return res } -public fun , T> D.write( - propertySpec: WritableDevicePropertySpec, - value: T, -): Job = launch { - propertySpec.write(value) +public suspend fun D.read(propertySpec: DevicePropertySpec): T = + readOrNull(propertySpec) ?: error("Failed to read property ${propertySpec.name} state") + +public operator fun D.get(propertySpec: DevicePropertySpec): T? = + getProperty(propertySpec.name)?.let(propertySpec.converter::metaToObject) + +/** + * Write typed property state and invalidate logical state + */ +@OptIn(InternalDeviceAPI::class) +public suspend fun D.write(propertySpec: WritableDevicePropertySpec, value: T) { + invalidate(propertySpec.name) + propertySpec.write(this, value) + //perform asynchronous read and update after write + launch { + read(propertySpec) + } } +/** + * Fire and forget variant of property writing. Actual write is performed asynchronously on a [Device] scope + */ +public operator fun D.set(propertySpec: WritableDevicePropertySpec, value: T): Unit { + launch { + write(propertySpec, value) + } +} + +/** + * Reset the logical state of a property + */ +public suspend fun D.invalidate(propertySpec: DevicePropertySpec) { + invalidate(propertySpec.name) +} + +public suspend fun D.execute(actionSpec: DeviceActionSpec, input: I? = null): O? = + actionSpec.execute(this, input) + /** * A type safe property change listener */ diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/spec/getDeviceProperty.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/spec/getDeviceProperty.kt deleted file mode 100644 index a7063f5..0000000 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/spec/getDeviceProperty.kt +++ /dev/null @@ -1,10 +0,0 @@ -package space.kscience.controls.spec - -import kotlinx.coroutines.runBlocking - -/** - * Blocking property get call - */ -public operator fun , T : Any> D.get( - propertySpec: DevicePropertySpec -): T? = runBlocking { read(propertySpec) } \ No newline at end of file diff --git a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDeviceBySpec.kt b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDeviceBySpec.kt index cc6044a..e1acd4b 100644 --- a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDeviceBySpec.kt +++ b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDeviceBySpec.kt @@ -1,6 +1,7 @@ package space.kscience.controls.modbus import com.ghgande.j2mod.modbus.facade.AbstractModbusMaster +import space.kscience.controls.api.Device import space.kscience.controls.api.DeviceHub import space.kscience.controls.spec.DeviceBySpec import space.kscience.controls.spec.DeviceSpec @@ -11,19 +12,19 @@ import space.kscience.dataforge.names.NameToken /** * A variant of [DeviceBySpec] that includes Modbus RTU/TCP/UDP client */ -public open class ModbusDeviceBySpec( +public open class ModbusDeviceBySpec( context: Context, - spec: DeviceSpec, + spec: DeviceSpec, override val clientId: Int, override val master: AbstractModbusMaster, meta: Meta = Meta.EMPTY, -) : ModbusDevice, DeviceBySpec(spec, context, meta) +) : ModbusDevice, DeviceBySpec(spec, context, meta) public class ModbusHub( public val context: Context, public val masterBuilder: () -> AbstractModbusMaster, - public val specs: Map>>, + public val specs: Map>>, ) : DeviceHub, AutoCloseable { public val master: AbstractModbusMaster by lazy(masterBuilder) diff --git a/controls-opcua/build.gradle.kts b/controls-opcua/build.gradle.kts index 1be4f75..02510e2 100644 --- a/controls-opcua/build.gradle.kts +++ b/controls-opcua/build.gradle.kts @@ -12,7 +12,6 @@ dependencies { api("org.eclipse.milo:sdk-client:$miloVersion") api("org.eclipse.milo:bsd-parser:$miloVersion") - api("org.eclipse.milo:sdk-server:$miloVersion") testImplementation(spclibs.kotlinx.coroutines.test) diff --git a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MiloDevice.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/OpcUaDevice.kt similarity index 90% rename from controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MiloDevice.kt rename to controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/OpcUaDevice.kt index 8132835..dd83e58 100644 --- a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MiloDevice.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/OpcUaDevice.kt @@ -18,7 +18,7 @@ import kotlin.reflect.KProperty /** * An OPC-UA device backed by Eclipse Milo client */ -public interface MiloDevice : Device { +public interface OpcUaDevice : Device { /** * The OPC-UA client initialized on first use */ @@ -29,7 +29,7 @@ public interface MiloDevice : Device { * Read OPC-UA value with timestamp * @param T the type of property to read. The value is coerced to it. */ -public suspend inline fun MiloDevice.readOpcWithTime( +public suspend inline fun OpcUaDevice.readOpcWithTime( nodeId: NodeId, converter: MetaConverter, magAge: Double = 500.0 @@ -50,7 +50,7 @@ public suspend inline fun MiloDevice.readOpcWithTime( /** * Read and coerce value from OPC-UA */ -public suspend inline fun MiloDevice.readOpc( +public suspend inline fun OpcUaDevice.readOpc( nodeId: NodeId, converter: MetaConverter, magAge: Double = 500.0 @@ -72,7 +72,7 @@ public suspend inline fun MiloDevice.readOpc( return converter.metaToObject(meta) ?: error("Meta $meta could not be converted to ${T::class}") } -public suspend inline fun MiloDevice.writeOpc( +public suspend inline fun OpcUaDevice.writeOpc( nodeId: NodeId, converter: MetaConverter, value: T @@ -85,7 +85,7 @@ public suspend inline fun MiloDevice.writeOpc( /** * A device-bound OPC-UA property. Does not trigger device properties change. */ -public inline fun MiloDevice.opc( +public inline fun OpcUaDevice.opc( nodeId: NodeId, converter: MetaConverter, magAge: Double = 500.0 @@ -104,7 +104,7 @@ public inline fun MiloDevice.opc( /** * Register a mutable OPC-UA based [Double] property in a device spec */ -public fun MiloDevice.opcDouble( +public fun OpcUaDevice.opcDouble( nodeId: NodeId, magAge: Double = 1.0 ): ReadWriteProperty = opc(nodeId, MetaConverter.double, magAge) @@ -112,7 +112,7 @@ public fun MiloDevice.opcDouble( /** * Register a mutable OPC-UA based [Int] property in a device spec */ -public fun MiloDevice.opcInt( +public fun OpcUaDevice.opcInt( nodeId: NodeId, magAge: Double = 1.0 ): ReadWriteProperty = opc(nodeId, MetaConverter.int, magAge) @@ -120,7 +120,7 @@ public fun MiloDevice.opcInt( /** * Register a mutable OPC-UA based [String] property in a device spec */ -public fun MiloDevice.opcString( +public fun OpcUaDevice.opcString( nodeId: NodeId, magAge: Double = 1.0 ): ReadWriteProperty = opc(nodeId, MetaConverter.string, magAge) \ No newline at end of file diff --git a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MiloDeviceBySpec.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/OpcUaDeviceBySpec.kt similarity index 91% rename from controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MiloDeviceBySpec.kt rename to controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/OpcUaDeviceBySpec.kt index 8fef3c1..821b693 100644 --- a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MiloDeviceBySpec.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/OpcUaDeviceBySpec.kt @@ -4,6 +4,7 @@ import org.eclipse.milo.opcua.sdk.client.OpcUaClient import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy +import space.kscience.controls.api.Device import space.kscience.controls.spec.DeviceBySpec import space.kscience.controls.spec.DeviceSpec import space.kscience.dataforge.context.Context @@ -40,14 +41,14 @@ public class MiloConfiguration : Scheme() { /** * A variant of [DeviceBySpec] that includes OPC-UA client */ -public open class MiloDeviceBySpec>( +public open class OpcUaDeviceBySpec( spec: DeviceSpec, config: MiloConfiguration, context: Context = Global, -) : MiloDevice, DeviceBySpec(spec, context, config.meta) { +) : OpcUaDevice, DeviceBySpec(spec, context, config.meta) { override val client: OpcUaClient by lazy { - context.createMiloClient( + context.createOpcUaClient( config.endpointUrl, securityPolicy = config.securityPolicy, identityProvider = config.username?.let { diff --git a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/miloClient.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/miloClient.kt index 8415b3a..face6cc 100644 --- a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/miloClient.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/miloClient.kt @@ -18,10 +18,10 @@ import java.nio.file.Path import java.nio.file.Paths import java.util.* -public fun T?.toOptional(): Optional = if(this == null) Optional.empty() else Optional.of(this) +internal fun T?.toOptional(): Optional = if(this == null) Optional.empty() else Optional.of(this) -internal fun Context.createMiloClient( +internal fun Context.createOpcUaClient( endpointUrl: String, //"opc.tcp://localhost:12686/milo" securityPolicy: SecurityPolicy = SecurityPolicy.Basic256Sha256, identityProvider: IdentityProvider = AnonymousProvider(), diff --git a/controls-opcua/src/test/kotlin/space/kscience/controls/opcua/client/OpcUaClientTest.kt b/controls-opcua/src/test/kotlin/space/kscience/controls/opcua/client/OpcUaClientTest.kt index 72cee67..d16cb1e 100644 --- a/controls-opcua/src/test/kotlin/space/kscience/controls/opcua/client/OpcUaClientTest.kt +++ b/controls-opcua/src/test/kotlin/space/kscience/controls/opcua/client/OpcUaClientTest.kt @@ -6,27 +6,31 @@ import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId import org.junit.jupiter.api.Test import space.kscience.controls.spec.DeviceSpec import space.kscience.controls.spec.doubleProperty +import space.kscience.controls.spec.read import space.kscience.dataforge.meta.transformations.MetaConverter class OpcUaClientTest { - class DemoMiloDevice(config: MiloConfiguration) : MiloDeviceBySpec(DemoMiloDevice, config) { + class DemoOpcUaDevice(config: MiloConfiguration) : OpcUaDeviceBySpec(DemoOpcUaDevice, config) { //val randomDouble by opcDouble(NodeId(2, "Dynamic/RandomDouble")) suspend fun readRandomDouble() = readOpc(NodeId(2, "Dynamic/RandomDouble"), MetaConverter.double) - companion object : DeviceSpec() { - fun build(): DemoMiloDevice { + companion object : DeviceSpec() { + /** + * Build a device. This is not a part of the specification + */ + fun build(): DemoOpcUaDevice { val config = MiloConfiguration { endpointUrl = "opc.tcp://milo.digitalpetri.com:62541/milo" } - return DemoMiloDevice(config) + return DemoOpcUaDevice(config) } - inline fun use(block: DemoMiloDevice.() -> R): R = build().use(block) + inline fun use(block: (DemoOpcUaDevice) -> R): R = build().use(block) - val randomDouble by doubleProperty(read = DemoMiloDevice::readRandomDouble) + val randomDouble by doubleProperty(read = DemoOpcUaDevice::readRandomDouble) } @@ -36,7 +40,9 @@ class OpcUaClientTest { @OptIn(ExperimentalCoroutinesApi::class) @Test fun testReadDouble() = runTest { - println(DemoMiloDevice.use { DemoMiloDevice.randomDouble.read() }) + DemoOpcUaDevice.use{ + println(it.read(DemoOpcUaDevice.randomDouble)) + } } } \ No newline at end of file diff --git a/controls-storage/controls-xodus/src/main/kotlin/space/kscience/controls/xodus/XodusDeviceMessageStorage.kt b/controls-storage/controls-xodus/src/main/kotlin/space/kscience/controls/xodus/XodusDeviceMessageStorage.kt index bccaee9..e5d2e4a 100644 --- a/controls-storage/controls-xodus/src/main/kotlin/space/kscience/controls/xodus/XodusDeviceMessageStorage.kt +++ b/controls-storage/controls-xodus/src/main/kotlin/space/kscience/controls/xodus/XodusDeviceMessageStorage.kt @@ -13,11 +13,11 @@ import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import space.kscience.controls.api.DeviceMessage import space.kscience.controls.storage.DeviceMessageStorage -import space.kscience.controls.storage.workDirectory import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.context.request import space.kscience.dataforge.io.IOPlugin +import space.kscience.dataforge.io.workDirectory import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.string diff --git a/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt b/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt deleted file mode 100644 index 0847055..0000000 --- a/controls-storage/src/jvmMain/kotlin/space/kscience/controls/storage/workDirectory.kt +++ /dev/null @@ -1,29 +0,0 @@ -package space.kscience.controls.storage - -import space.kscience.dataforge.context.ContextBuilder -import space.kscience.dataforge.io.IOPlugin -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.set -import space.kscience.dataforge.meta.string -import java.nio.file.Path -import kotlin.io.path.Path - - -public val IOPlugin.workDirectory: Path - get() { - val workDirectoryPath = meta[IOPlugin.WORK_DIRECTORY_KEY].string - ?: context.properties[IOPlugin.WORK_DIRECTORY_KEY].string - ?: ".dataforge" - - return Path(workDirectoryPath) - } - -public fun ContextBuilder.workDirectory(path: String) { - properties { - set(IOPlugin.WORK_DIRECTORY_KEY, path) - } -} - -public fun ContextBuilder.workDirectory(path: Path){ - workDirectory(path.toAbsolutePath().toString()) -} diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt index 802fc4d..538570d 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoControllerView.kt @@ -17,6 +17,7 @@ import space.kscience.controls.manager.install import space.kscience.controls.opcua.server.OpcUaServer import space.kscience.controls.opcua.server.endpoint import space.kscience.controls.opcua.server.serveDevices +import space.kscience.controls.spec.write import space.kscience.dataforge.context.* import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.rsocket.rSocketWithTcp @@ -125,9 +126,9 @@ class DemoControllerView : View(title = " Demo controller remote") { action { controller.device?.run { launch { - timeScale.write(timeScaleSlider.value) - sinScale.write(xScaleSlider.value) - cosScale.write(yScaleSlider.value) + write(timeScale, timeScaleSlider.value) + write(sinScale, xScaleSlider.value) + write(cosScale, yScaleSlider.value) } } } diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt index 10713f3..1a8ad97 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/DemoDevice.kt @@ -62,21 +62,21 @@ class DemoDevice(context: Context, meta: Meta) : DeviceBySpec(DemoDe override suspend fun DemoDevice.onOpen() { launch { - sinScale.read() - cosScale.read() - timeScale.read() + read(sinScale) + read(cosScale) + read(timeScale) } doRecurring(50.milliseconds) { - sin.read() - cos.read() - coordinates.read() + read(sin) + read(cos) + read(coordinates) } } val resetScale by action(MetaConverter.meta, MetaConverter.meta) { - timeScale.write(5000.0) - sinScale.write(1.0) - cosScale.write(1.0) + write(timeScale, 5000.0) + write(sinScale, 1.0) + write(cosScale, 1.0) null } diff --git a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt index 5cf3386..addc3a9 100644 --- a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/MagixVirtualCar.kt @@ -3,6 +3,7 @@ package space.kscience.controls.demo.car import kotlinx.coroutines.launch import space.kscience.controls.api.PropertyChangedMessage import space.kscience.controls.client.controlsMagixFormat +import space.kscience.controls.spec.write import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta @@ -21,7 +22,7 @@ class MagixVirtualCar(context: Context, meta: Meta) : VirtualCar(context, meta) (payload as? PropertyChangedMessage)?.let { message -> if (message.sourceDevice == Name.parse("virtual-car")) { when (message.property) { - "acceleration" -> IVirtualCar.acceleration.write(Vector2D.metaToObject(message.value)) + "acceleration" -> write(IVirtualCar.acceleration, Vector2D.metaToObject(message.value)) } } } diff --git a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCar.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCar.kt index db0638c..1f1dc69 100644 --- a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCar.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCar.kt @@ -8,6 +8,7 @@ import kotlinx.datetime.Clock import kotlinx.datetime.Instant import space.kscience.controls.spec.DeviceBySpec import space.kscience.controls.spec.doRecurring +import space.kscience.controls.spec.read import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory import space.kscience.dataforge.meta.Meta @@ -78,9 +79,9 @@ open class VirtualCar(context: Context, meta: Meta) : DeviceBySpec(I //TODO apply friction. One can introduce rotation of the cabin and different friction coefficients along the axis launch { //update logical states - IVirtualCar.location.read() - IVirtualCar.speed.read() - IVirtualCar.acceleration.read() + read(IVirtualCar.location) + read(IVirtualCar.speed) + read(IVirtualCar.acceleration) } } diff --git a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt index bde7e6b..f68c585 100644 --- a/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt +++ b/demo/car/src/main/kotlin/space/kscience/controls/demo/car/VirtualCarController.kt @@ -12,6 +12,7 @@ import space.kscience.controls.client.connectToMagix import space.kscience.controls.demo.car.IVirtualCar.Companion.acceleration import space.kscience.controls.manager.DeviceManager import space.kscience.controls.manager.install +import space.kscience.controls.spec.write import space.kscience.controls.storage.storeMessages import space.kscience.controls.xodus.XodusDeviceMessageStorage import space.kscience.dataforge.context.* @@ -113,7 +114,8 @@ class VirtualCarControllerView : View(title = " Virtual car controller remote") action { controller.virtualCar?.run { launch { - acceleration.write( + write( + acceleration, Vector2D( accelerationXProperty.get(), accelerationYProperty.get() diff --git a/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/MksPdr900Device.kt b/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/MksPdr900Device.kt index 304102e..949ced9 100644 --- a/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/MksPdr900Device.kt +++ b/demo/mks-pdr900/src/main/kotlin/center/sciprog/devices/mks/MksPdr900Device.kt @@ -45,7 +45,7 @@ class MksPdr900Device(context: Context, meta: Meta) : DeviceBySpec() { @@ -336,15 +336,15 @@ class PiMotionMasterDevice( val move by metaAction { val target = it.double ?: it?.get("target").double ?: error("Unacceptable target value $it") - closedLoop.write(true) + write(closedLoop, true) //optionally set velocity it?.get("velocity").double?.let { v -> - velocity.write(v) + write(velocity, v) } - targetPosition.write(target) + write(targetPosition, target) //read `onTarget` and `position` properties in a cycle until movement is complete - while (!onTarget.read()) { - position.read() + while (!read(onTarget)) { + read(position) delay(200) } null diff --git a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt index 8e399a4..e8b4e68 100644 --- a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt +++ b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/fxDeviceProperties.kt @@ -59,7 +59,7 @@ fun D.fxProperty(spec: WritableDevicePropertySpec): onChange { newValue -> if (newValue != null) { - write(spec, newValue) + set(spec, newValue) } } }