From a6bf9b8db60e9b8129d7cff9d0096f83d61d489c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 18 May 2023 09:30:26 +0300 Subject: [PATCH 01/43] Generalize nio ports architecture. Add Udp(datagram) port --- .../ports/{TcpPort.kt => ChannelPort.kt} | 82 +++++++++++++------ .../{TcpPortPlugin.kt => JvmPortsPlugin.kt} | 18 ++-- 2 files changed, 70 insertions(+), 30 deletions(-) rename controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/{TcpPort.kt => ChannelPort.kt} (55%) rename controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/{TcpPortPlugin.kt => JvmPortsPlugin.kt} (51%) diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt similarity index 55% rename from controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt rename to controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt index 77fec44..79651d5 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPort.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt @@ -10,6 +10,8 @@ import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.string import java.net.InetSocketAddress import java.nio.ByteBuffer +import java.nio.channels.ByteChannel +import java.nio.channels.DatagramChannel import java.nio.channels.SocketChannel import kotlin.coroutines.CoroutineContext @@ -21,19 +23,17 @@ internal fun ByteBuffer.readArray(limit: Int = limit()): ByteArray { return response } -public class TcpPort private constructor( +/** + * A port based on nio [ByteChannel] + */ +public class ChannelPort ( context: Context, - public val host: String, - public val port: Int, coroutineContext: CoroutineContext = context.coroutineContext, + channelBuilder: suspend () -> ByteChannel, ) : AbstractPort(context, coroutineContext), AutoCloseable { - override fun toString(): String = "port[tcp:$host:$port]" - - private val futureChannel: Deferred = this.scope.async(Dispatchers.IO) { - SocketChannel.open(InetSocketAddress(host, port)).apply { - configureBlocking(false) - } + private val futureChannel: Deferred = this.scope.async(Dispatchers.IO) { + channelBuilder() } /** @@ -58,7 +58,7 @@ public class TcpPort private constructor( } } - override suspend fun write(data: ByteArray): Unit = withContext(Dispatchers.IO){ + override suspend fun write(data: ByteArray): Unit = withContext(Dispatchers.IO) { futureChannel.await().write(ByteBuffer.wrap(data)) } @@ -72,24 +72,56 @@ public class TcpPort private constructor( } super.close() } +} - public companion object : PortFactory { +/** + * A [PortFactory] for TCP connections + */ +public object TcpPort : PortFactory { - override val type: String = "tcp" + override val type: String = "tcp" - public fun open( - context: Context, - host: String, - port: Int, - coroutineContext: CoroutineContext = context.coroutineContext, - ): TcpPort { - return TcpPort(context, host, port, coroutineContext) - } - - override fun build(context: Context, meta: Meta): Port { - val host = meta["host"].string ?: "localhost" - val port = meta["port"].int ?: error("Port value for TCP port is not defined in $meta") - return open(context, host, port) + public fun open( + context: Context, + host: String, + port: Int, + coroutineContext: CoroutineContext = context.coroutineContext, + ): ChannelPort = ChannelPort(context,coroutineContext){ + SocketChannel.open(InetSocketAddress(host, port)).apply { + configureBlocking(false) } } + + override fun build(context: Context, meta: Meta): ChannelPort { + val host = meta["host"].string ?: "localhost" + val port = meta["port"].int ?: error("Port value for TCP port is not defined in $meta") + return open(context, host, port) + } +} + + +/** + * A [PortFactory] for UDP connections + */ +public object UdpPort : PortFactory { + + override val type: String = "udp" + + public fun open( + context: Context, + host: String, + port: Int, + coroutineContext: CoroutineContext = context.coroutineContext, + ): ChannelPort = ChannelPort(context,coroutineContext){ + DatagramChannel.open().apply { + bind(InetSocketAddress(host, port)) + configureBlocking(false) + } + } + + override fun build(context: Context, meta: Meta): ChannelPort { + val host = meta["host"].string ?: "localhost" + val port = meta["port"].int ?: error("Port value for UDP port is not defined in $meta") + return open(context, host, port) + } } \ No newline at end of file diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPortPlugin.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/JvmPortsPlugin.kt similarity index 51% rename from controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPortPlugin.kt rename to controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/JvmPortsPlugin.kt index 592d0c3..d9d87e2 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/TcpPortPlugin.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/JvmPortsPlugin.kt @@ -6,21 +6,29 @@ import space.kscience.dataforge.context.PluginFactory import space.kscience.dataforge.context.PluginTag import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.parseAsName -public class TcpPortPlugin : AbstractPlugin() { +/** + * A plugin for loading JVM nio-based ports + */ +public class JvmPortsPlugin : AbstractPlugin() { + public val ports: Ports by require(Ports) override val tag: PluginTag get() = Companion.tag override fun content(target: String): Map = when(target){ - PortFactory.TYPE -> mapOf(Name.EMPTY to TcpPort) + PortFactory.TYPE -> mapOf( + TcpPort.type.parseAsName() to TcpPort, + UdpPort.type.parseAsName() to UdpPort + ) else -> emptyMap() } - public companion object : PluginFactory { + public companion object : PluginFactory { - override val tag: PluginTag = PluginTag("controls.ports.tcp", group = PluginTag.DATAFORGE_GROUP) + override val tag: PluginTag = PluginTag("controls.ports.jvm", group = PluginTag.DATAFORGE_GROUP) - override fun build(context: Context, meta: Meta): TcpPortPlugin = TcpPortPlugin() + override fun build(context: Context, meta: Meta): JvmPortsPlugin = JvmPortsPlugin() } From 28cb9af2675dd4aec9540ab6029722c1ba6a6f9e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 18 May 2023 09:50:39 +0300 Subject: [PATCH 02/43] [WIP] PI --- controls-pi/build.gradle.kts | 12 +++++++++ .../kscience/controls/pi/PiSerialPort.kt | 25 +++++++++++++++++++ settings.gradle.kts | 1 + 3 files changed, 38 insertions(+) create mode 100644 controls-pi/build.gradle.kts create mode 100644 controls-pi/src/main/kotlin/space/kscience/controls/pi/PiSerialPort.kt diff --git a/controls-pi/build.gradle.kts b/controls-pi/build.gradle.kts new file mode 100644 index 0000000..3fa0dbc --- /dev/null +++ b/controls-pi/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("space.kscience.gradle.jvm") + `maven-publish` +} + +dependencies{ + api(project(":controls-core")) + implementation("com.pi4j:pi4j-ktx:2.4.0") // Kotlin DSL + implementation("com.pi4j:pi4j-core:2.3.0") + implementation("com.pi4j:pi4j-plugin-raspberrypi:2.3.0") + implementation("com.pi4j:pi4j-plugin-pigpio:2.3.0") +} \ No newline at end of file diff --git a/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiSerialPort.kt b/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiSerialPort.kt new file mode 100644 index 0000000..4cdee6d --- /dev/null +++ b/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiSerialPort.kt @@ -0,0 +1,25 @@ +package space.kscience.controls.pi + +import com.pi4j.io.serial.FlowControl +import com.pi4j.io.serial.Parity +import com.pi4j.io.serial.Serial +import com.pi4j.io.serial.StopBits +import com.pi4j.ktx.io.open +import com.pi4j.ktx.io.piGpioSerialProvider +import com.pi4j.ktx.io.serial +import space.kscience.controls.ports.AbstractPort +import space.kscience.dataforge.context.Context +import kotlin.coroutines.CoroutineContext + +public class PiSerialPort( + context: Context, + coroutineContext: CoroutineContext = context.coroutineContext, + public val serialBuilder: () -> Serial, +) : AbstractPort(context, coroutineContext) { + + private val serial by lazy { serialBuilder() } + + override suspend fun write(data: ByteArray) { + TODO() + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 21b2467..72a29bd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -43,6 +43,7 @@ include( ":controls-core", ":controls-ktor-tcp", ":controls-serial", + ":controls-pi", ":controls-server", ":controls-opcua", ":controls-modbus", From a5a2edc81a9348d197f443a6d4e0cf600354f999 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 19 May 2023 20:27:08 +0300 Subject: [PATCH 03/43] Add Pi serial port --- .../kscience/controls/ports/ChannelPort.kt | 4 +- .../space/kscience/controls/pi/PiPlugin.kt | 19 ++++++ .../kscience/controls/pi/PiSerialPort.kt | 58 ++++++++++++++++--- 3 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 controls-pi/src/main/kotlin/space/kscience/controls/pi/PiPlugin.kt diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt index 79651d5..0a9a0f0 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt @@ -15,7 +15,7 @@ import java.nio.channels.DatagramChannel import java.nio.channels.SocketChannel import kotlin.coroutines.CoroutineContext -internal fun ByteBuffer.readArray(limit: Int = limit()): ByteArray { +public fun ByteBuffer.toArray(limit: Int = limit()): ByteArray { rewind() val response = ByteArray(limit) get(response) @@ -48,7 +48,7 @@ public class ChannelPort ( try { val num = channel.read(buffer) if (num > 0) { - receive(buffer.readArray(num)) + receive(buffer.toArray(num)) } if (num < 0) cancel("The input channel is exhausted") } catch (ex: Exception) { diff --git a/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiPlugin.kt b/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiPlugin.kt new file mode 100644 index 0000000..e3d6a0a --- /dev/null +++ b/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiPlugin.kt @@ -0,0 +1,19 @@ +package space.kscience.controls.pi + +import space.kscience.dataforge.context.AbstractPlugin +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.PluginFactory +import space.kscience.dataforge.context.PluginTag +import space.kscience.dataforge.meta.Meta + +public class PiPlugin : AbstractPlugin() { + override val tag: PluginTag get() = Companion.tag + + public companion object : PluginFactory { + + override val tag: PluginTag = PluginTag("controls.ports.pi", group = PluginTag.DATAFORGE_GROUP) + + override fun build(context: Context, meta: Meta): PiPlugin = PiPlugin() + + } +} \ No newline at end of file diff --git a/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiSerialPort.kt b/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiSerialPort.kt index 4cdee6d..0437698 100644 --- a/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiSerialPort.kt +++ b/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiSerialPort.kt @@ -1,14 +1,19 @@ package space.kscience.controls.pi -import com.pi4j.io.serial.FlowControl -import com.pi4j.io.serial.Parity +import com.pi4j.Pi4J import com.pi4j.io.serial.Serial -import com.pi4j.io.serial.StopBits -import com.pi4j.ktx.io.open -import com.pi4j.ktx.io.piGpioSerialProvider +import com.pi4j.io.serial.SerialConfigBuilder import com.pi4j.ktx.io.serial +import kotlinx.coroutines.* import space.kscience.controls.ports.AbstractPort +import space.kscience.controls.ports.Port +import space.kscience.controls.ports.PortFactory +import space.kscience.controls.ports.toArray import space.kscience.dataforge.context.Context +import space.kscience.dataforge.context.error +import space.kscience.dataforge.context.logger +import space.kscience.dataforge.meta.Meta +import java.nio.ByteBuffer import kotlin.coroutines.CoroutineContext public class PiSerialPort( @@ -17,9 +22,44 @@ public class PiSerialPort( public val serialBuilder: () -> Serial, ) : AbstractPort(context, coroutineContext) { - private val serial by lazy { serialBuilder() } + private val serial: Serial by lazy { serialBuilder() } - override suspend fun write(data: ByteArray) { - TODO() + + private val listenerJob = this.scope.launch(Dispatchers.IO) { + val buffer = ByteBuffer.allocate(1024) + while (isActive) { + try { + val num = serial.read(buffer) + if (num > 0) { + receive(buffer.toArray(num)) + } + if (num < 0) cancel("The input channel is exhausted") + } catch (ex: Exception) { + logger.error(ex) { "Channel read error" } + delay(1000) + } + } } -} \ No newline at end of file + + override suspend fun write(data: ByteArray): Unit = withContext(Dispatchers.IO) { + serial.write(data) + } + + override fun close() { + listenerJob.cancel() + serial.close() + } + + public companion object : PortFactory { + override val type: String get() = "pi" + + public fun open(context: Context, device: String, block: SerialConfigBuilder.() -> Unit): PiSerialPort = + PiSerialPort(context) { Pi4J.newAutoContext().serial(device, block) } + + override fun build(context: Context, meta: Meta): Port = PiSerialPort(context) { + Pi4J.newAutoContext().serial() + } + + } +} + From e7d02be8493a7b9fc70518b23bec53f885391e79 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 19 May 2023 21:55:21 +0300 Subject: [PATCH 04/43] Add Pi serial port --- .../kotlin/space/kscience/controls/ports/Port.kt | 2 +- .../space/kscience/controls/ports/phrases.kt | 5 +++++ .../space/kscience/controls/pi/PiSerialPort.kt | 14 ++++++++++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt index 4fad1db..db19817 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt @@ -82,7 +82,7 @@ public abstract class AbstractPort( /** * Raw flow of incoming data chunks. The chunks are not guaranteed to be complete phrases. * In order to form phrases, some condition should be used on top of it. - * For example [delimitedIncoming] generates phrases with fixed delimiter. + * For example [stringsDelimitedIncoming] generates phrases with fixed delimiter. */ override fun receiving(): Flow = incoming.receiveAsFlow() diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt index 21afa8d..1214d01 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/phrases.kt @@ -48,3 +48,8 @@ public fun Flow.withStringDelimiter(delimiter: String): Flow * A flow of delimited phrases */ public fun Port.delimitedIncoming(delimiter: ByteArray): Flow = receiving().withDelimiter(delimiter) + +/** + * A flow of delimited phrases with string content + */ +public fun Port.stringsDelimitedIncoming(delimiter: String): Flow = receiving().withStringDelimiter(delimiter) diff --git a/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiSerialPort.kt b/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiSerialPort.kt index 0437698..4924b8d 100644 --- a/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiSerialPort.kt +++ b/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiSerialPort.kt @@ -1,6 +1,7 @@ package space.kscience.controls.pi import com.pi4j.Pi4J +import com.pi4j.io.serial.Baud import com.pi4j.io.serial.Serial import com.pi4j.io.serial.SerialConfigBuilder import com.pi4j.ktx.io.serial @@ -13,6 +14,9 @@ import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.error import space.kscience.dataforge.context.logger import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.enum +import space.kscience.dataforge.meta.get +import space.kscience.dataforge.meta.string import java.nio.ByteBuffer import kotlin.coroutines.CoroutineContext @@ -54,10 +58,16 @@ public class PiSerialPort( override val type: String get() = "pi" public fun open(context: Context, device: String, block: SerialConfigBuilder.() -> Unit): PiSerialPort = - PiSerialPort(context) { Pi4J.newAutoContext().serial(device, block) } + PiSerialPort(context) { + Pi4J.newAutoContext().serial(device, block) + } override fun build(context: Context, meta: Meta): Port = PiSerialPort(context) { - Pi4J.newAutoContext().serial() + val device: String = meta["device"].string ?: error("Device name not defined") + val baudRate: Baud = meta["baudRate"].enum() ?: Baud._9600 + Pi4J.newAutoContext().serial(device) { + baud8N1(baudRate) + } } } From b8a82feed09102033d1b827ddf5c02f695459758 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 21 May 2023 16:53:42 +0300 Subject: [PATCH 05/43] Modbus-based implementation of controller --- .../kscience/controls/modbus/ModbusDevice.kt | 104 ++++++++++++++---- .../controls/modbus/ModbusRegistryMap.kt | 48 ++++++++ 2 files changed, 130 insertions(+), 22 deletions(-) diff --git a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDevice.kt b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDevice.kt index 04bb6a0..1c442aa 100644 --- a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDevice.kt +++ b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDevice.kt @@ -5,6 +5,10 @@ import com.ghgande.j2mod.modbus.procimg.InputRegister import com.ghgande.j2mod.modbus.procimg.Register import com.ghgande.j2mod.modbus.procimg.SimpleRegister import com.ghgande.j2mod.modbus.util.BitVector +import io.ktor.utils.io.core.ByteReadPacket +import io.ktor.utils.io.core.buildPacket +import io.ktor.utils.io.core.readByteBuffer +import io.ktor.utils.io.core.writeShort import space.kscience.controls.api.Device import java.nio.ByteBuffer import kotlin.properties.ReadWriteProperty @@ -22,9 +26,56 @@ public interface ModbusDevice : Device { public val clientId: Int /** - * The OPC-UA client initialized on first use + * The modubus master connector */ public val master: AbstractModbusMaster + + public operator fun ModbusRegistryKey.Coil.getValue(thisRef: Any?, property: KProperty<*>): Boolean = + readCoil(address) + + public operator fun ModbusRegistryKey.Coil.setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { + writeCoil(address, value) + } + + public operator fun ModbusRegistryKey.DiscreteInput.getValue(thisRef: Any?, property: KProperty<*>): Boolean = + readInputDiscrete(address) + + public operator fun ModbusRegistryKey.InputRegister.getValue(thisRef: Any?, property: KProperty<*>): Short = + readInputRegister(address) + + public operator fun ModbusRegistryKey.InputRange.getValue(thisRef: Any?, property: KProperty<*>): T { + val packet = readInputRegistersToPacket(address, count) + return format.readObject(packet) + } + + + public operator fun ModbusRegistryKey.HoldingRegister.getValue(thisRef: Any?, property: KProperty<*>): Short = + readHoldingRegister(address) + + public operator fun ModbusRegistryKey.HoldingRegister.setValue( + thisRef: Any?, + property: KProperty<*>, + value: Short, + ) { + writeHoldingRegister(address, value) + } + + public operator fun ModbusRegistryKey.HoldingRange.getValue(thisRef: Any?, property: KProperty<*>): T { + val packet = readInputRegistersToPacket(address, count) + return format.readObject(packet) + } + + public operator fun ModbusRegistryKey.HoldingRange.setValue( + thisRef: Any?, + property: KProperty<*>, + value: T, + ) { + val buffer = buildPacket { + format.writeObject(this, value) + }.readByteBuffer() + writeHoldingRegisters(address,buffer) + } + } /** @@ -48,9 +99,16 @@ public fun ModbusDevice.writeCoil(ref: Int, value: Boolean) { master.writeCoil(clientId, ref, value) } +public fun ModbusDevice.writeCoil(key: ModbusRegistryKey.Coil, value: Boolean) { + master.writeCoil(clientId, key.address, value) +} + public fun ModbusDevice.readInputDiscretes(ref: Int, count: Int): BitVector = master.readInputDiscretes(clientId, ref, count) +public fun ModbusDevice.readInputDiscrete(ref: Int): Boolean = + master.readInputDiscretes(clientId, ref, 1).getBit(0) + public fun ModbusDevice.readInputRegisters(ref: Int, count: Int): List = master.readInputRegisters(clientId, ref, count).toList() @@ -64,25 +122,42 @@ private fun Array.toBuffer(): ByteBuffer { return buffer } +private fun Array.toPacket(): ByteReadPacket = buildPacket { + forEach { value -> + writeShort(value.toShort()) + } +} + public fun ModbusDevice.readInputRegistersToBuffer(ref: Int, count: Int): ByteBuffer = master.readInputRegisters(clientId, ref, count).toBuffer() +public fun ModbusDevice.readInputRegistersToPacket(ref: Int, count: Int): ByteReadPacket = + master.readInputRegisters(clientId, ref, count).toPacket() + public fun ModbusDevice.readDoubleInput(ref: Int): Double = readInputRegistersToBuffer(ref, Double.SIZE_BYTES).getDouble() -public fun ModbusDevice.readShortInput(ref: Int): Short = +public fun ModbusDevice.readInputRegister(ref: Int): Short = readInputRegisters(ref, 1).first().toShort() public fun ModbusDevice.readHoldingRegisters(ref: Int, count: Int): List = master.readMultipleRegisters(clientId, ref, count).toList() +/** + * Read a number of registers to a [ByteBuffer] + * @param ref address of a register + * @param count number of 2-bytes registers to read. Buffer size is 2*[count] + */ public fun ModbusDevice.readHoldingRegistersToBuffer(ref: Int, count: Int): ByteBuffer = master.readMultipleRegisters(clientId, ref, count).toBuffer() +public fun ModbusDevice.readHoldingRegistersToPacket(ref: Int, count: Int): ByteReadPacket = + master.readMultipleRegisters(clientId, ref, count).toPacket() + public fun ModbusDevice.readDoubleRegister(ref: Int): Double = readHoldingRegistersToBuffer(ref, Double.SIZE_BYTES).getDouble() -public fun ModbusDevice.readShortRegister(ref: Int): Short = +public fun ModbusDevice.readHoldingRegister(ref: Int): Short = readHoldingRegisters(ref, 1).first().toShort() public fun ModbusDevice.writeHoldingRegisters(ref: Int, values: ShortArray): Int = @@ -100,7 +175,7 @@ public fun ModbusDevice.writeHoldingRegister(ref: Int, value: Short): Int = ) public fun ModbusDevice.writeHoldingRegisters(ref: Int, buffer: ByteBuffer): Int { - val array = ShortArray(buffer.limit().floorDiv(2)) { buffer.getShort(it * 2) } + val array: ShortArray = ShortArray(buffer.limit().floorDiv(2)) { buffer.getShort(it * 2) } return writeHoldingRegisters(ref, array) } @@ -109,17 +184,17 @@ public fun ModbusDevice.writeShortRegister(ref: Int, value: Short) { master.writeSingleRegister(ref, SimpleRegister().apply { setValue(value) }) } -public fun ModbusDevice.modBusRegister( +public fun ModbusDevice.modbusRegister( ref: Int, ): ReadWriteProperty = object : ReadWriteProperty { - override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Short = readShortRegister(ref) + override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Short = readHoldingRegister(ref) override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Short) { writeHoldingRegister(ref, value) } } -public fun ModbusDevice.modBusDoubleRegister( +public fun ModbusDevice.modbusDoubleRegister( ref: Int, ): ReadWriteProperty = object : ReadWriteProperty { override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Double = readDoubleRegister(ref) @@ -129,18 +204,3 @@ public fun ModbusDevice.modBusDoubleRegister( writeHoldingRegisters(ref, buffer) } } - - -// -//public inline fun ModbusDevice.opcDouble( -//): ReadWriteProperty = ma -// -//public inline fun ModbusDeviceBySpec<*>.opcInt( -// nodeId: NodeId, -// magAge: Double = 1.0, -//): ReadWriteProperty = opc(nodeId, MetaConverter.int, magAge) -// -//public inline fun ModbusDeviceBySpec<*>.opcString( -// nodeId: NodeId, -// magAge: Double = 1.0, -//): ReadWriteProperty = opc(nodeId, MetaConverter.string, magAge) diff --git a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt index e365a88..ee7b8c3 100644 --- a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt +++ b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt @@ -1,5 +1,8 @@ package space.kscience.controls.modbus +import space.kscience.dataforge.io.IOFormat +import space.kscience.dataforge.io.IOReader + public sealed class ModbusRegistryKey { /** @@ -20,25 +23,70 @@ public sealed class ModbusRegistryKey { } } + /** + * Read-only binary value + */ public class InputRegister(public val address: Int) : ModbusRegistryKey() { init { require(address in 20001..29999) { "InputRegister address must be in 20001..29999 range" } } } + public class InputRange(public val address: Int, public val count: Int, public val format: IOReader) { + public val endAddress: Int get() = address + count + + init { + require(address in 20001..29999) { "InputRange begin address is $address, but must be in 20001..29999 range" } + require(endAddress in 20001..29999) { "InputRange end address is ${endAddress}, but must be in 20001..29999 range" } + } + } + public class HoldingRegister(public val address: Int) : ModbusRegistryKey() { init { require(address in 30001..39999) { "HoldingRegister address must be in 30001..39999 range" } } } + + public class HoldingRange(public val address: Int, public val count: Int, public val format: IOFormat) { + public val endAddress: Int get() = address + count + + init { + require(address in 30001..39999) { "HoldingRange begin address is $address, but must be in 30001..39999 range" } + require(endAddress in 30001..39999) { "HoldingRange end address is ${endAddress}, but must be in 30001..39999 range" } + } + } } public abstract class ModbusRegistryMap { protected fun coil(address: Int): ModbusRegistryKey.Coil = ModbusRegistryKey.Coil(address) + protected fun coilByOffset(offset: Int): ModbusRegistryKey.Coil = ModbusRegistryKey.Coil(offset) + protected fun discrete(address: Int): ModbusRegistryKey.DiscreteInput = ModbusRegistryKey.DiscreteInput(address) + protected fun discreteByOffset(offset: Int): ModbusRegistryKey.DiscreteInput = + ModbusRegistryKey.DiscreteInput(10000 + offset) + protected fun input(address: Int): ModbusRegistryKey.InputRegister = ModbusRegistryKey.InputRegister(address) + protected fun input(address: Int, count: Int, reader: IOReader): ModbusRegistryKey.InputRange = + ModbusRegistryKey.InputRange(address, count, reader) + + + protected fun inputByOffset(offset: Int): ModbusRegistryKey.InputRegister = + ModbusRegistryKey.InputRegister(20000 + offset) + + protected fun inputByOffset(offset: Int, count: Int, reader: IOReader): ModbusRegistryKey.InputRange = + ModbusRegistryKey.InputRange(20000 + offset, count, reader) + protected fun register(address: Int): ModbusRegistryKey.HoldingRegister = ModbusRegistryKey.HoldingRegister(address) + + protected fun register(address: Int, count: Int, format: IOFormat): ModbusRegistryKey.HoldingRange = + ModbusRegistryKey.HoldingRange(address, count, format) + + protected fun registerByOffset(offset: Int): ModbusRegistryKey.HoldingRegister = + ModbusRegistryKey.HoldingRegister(30000 + offset) + + protected fun registerByOffset(offset: Int, count: Int, format: IOFormat): ModbusRegistryKey.HoldingRange = + ModbusRegistryKey.HoldingRange(offset + 30000, count, format) } From 0612c6e3a28473b33d4a0b1cc725f63024ba6f50 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 21 May 2023 17:36:26 +0300 Subject: [PATCH 06/43] Modbus registry validation and print --- .../controls/modbus/ModbusRegistryMap.kt | 122 ++++++++++++++---- .../controls/opcua/client/OpcUaClientTest.kt | 2 + 2 files changed, 99 insertions(+), 25 deletions(-) diff --git a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt index ee7b8c3..d367397 100644 --- a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt +++ b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt @@ -5,10 +5,13 @@ import space.kscience.dataforge.io.IOReader public sealed class ModbusRegistryKey { + public abstract val address: Int + public open val count: Int = 1 + /** * Read-only boolean value */ - public class Coil(public val address: Int) : ModbusRegistryKey() { + public data class Coil(override val address: Int) : ModbusRegistryKey() { init { require(address in 1..9999) { "Coil address must be in 1..9999 range" } } @@ -17,7 +20,7 @@ public sealed class ModbusRegistryKey { /** * Read-write boolean value */ - public class DiscreteInput(public val address: Int) : ModbusRegistryKey() { + public data class DiscreteInput(override val address: Int) : ModbusRegistryKey() { init { require(address in 10001..19999) { "DiscreteInput address must be in 10001..19999 range" } } @@ -26,13 +29,17 @@ public sealed class ModbusRegistryKey { /** * Read-only binary value */ - public class InputRegister(public val address: Int) : ModbusRegistryKey() { + public data class InputRegister(override val address: Int) : ModbusRegistryKey() { init { require(address in 20001..29999) { "InputRegister address must be in 20001..29999 range" } } } - public class InputRange(public val address: Int, public val count: Int, public val format: IOReader) { + public data class InputRange( + override val address: Int, + override val count: Int, + public val format: IOReader, + ) : ModbusRegistryKey() { public val endAddress: Int get() = address + count init { @@ -41,13 +48,17 @@ public sealed class ModbusRegistryKey { } } - public class HoldingRegister(public val address: Int) : ModbusRegistryKey() { + public data class HoldingRegister(override val address: Int) : ModbusRegistryKey() { init { require(address in 30001..39999) { "HoldingRegister address must be in 30001..39999 range" } } } - public class HoldingRange(public val address: Int, public val count: Int, public val format: IOFormat) { + public data class HoldingRange( + override val address: Int, + override val count: Int, + public val format: IOFormat, + ) : ModbusRegistryKey() { public val endAddress: Int get() = address + count init { @@ -58,35 +69,96 @@ public sealed class ModbusRegistryKey { } public abstract class ModbusRegistryMap { - protected fun coil(address: Int): ModbusRegistryKey.Coil = ModbusRegistryKey.Coil(address) - protected fun coilByOffset(offset: Int): ModbusRegistryKey.Coil = ModbusRegistryKey.Coil(offset) + private val _entries: MutableMap = mutableMapOf() - protected fun discrete(address: Int): ModbusRegistryKey.DiscreteInput = ModbusRegistryKey.DiscreteInput(address) + public val entries: Map get() = _entries - protected fun discreteByOffset(offset: Int): ModbusRegistryKey.DiscreteInput = - ModbusRegistryKey.DiscreteInput(10000 + offset) + protected fun register(key: T, description: String): T { + _entries[key] = description + return key + } - protected fun input(address: Int): ModbusRegistryKey.InputRegister = ModbusRegistryKey.InputRegister(address) + protected fun coil(address: Int, description: String = ""): ModbusRegistryKey.Coil = + register(ModbusRegistryKey.Coil(address), description) - protected fun input(address: Int, count: Int, reader: IOReader): ModbusRegistryKey.InputRange = - ModbusRegistryKey.InputRange(address, count, reader) + protected fun coilByOffset(offset: Int, description: String = ""): ModbusRegistryKey.Coil = + register(ModbusRegistryKey.Coil(offset), description) + protected fun discrete(address: Int, description: String = ""): ModbusRegistryKey.DiscreteInput = + register(ModbusRegistryKey.DiscreteInput(address), description) - protected fun inputByOffset(offset: Int): ModbusRegistryKey.InputRegister = - ModbusRegistryKey.InputRegister(20000 + offset) + protected fun discreteByOffset(offset: Int, description: String = ""): ModbusRegistryKey.DiscreteInput = + register(ModbusRegistryKey.DiscreteInput(10000 + offset), description) - protected fun inputByOffset(offset: Int, count: Int, reader: IOReader): ModbusRegistryKey.InputRange = - ModbusRegistryKey.InputRange(20000 + offset, count, reader) + protected fun input(address: Int, description: String = ""): ModbusRegistryKey.InputRegister = + register(ModbusRegistryKey.InputRegister(address), description) - protected fun register(address: Int): ModbusRegistryKey.HoldingRegister = ModbusRegistryKey.HoldingRegister(address) + protected fun input( + address: Int, + count: Int, + reader: IOReader, + description: String = "", + ): ModbusRegistryKey.InputRange = + register(ModbusRegistryKey.InputRange(address, count, reader), description) - protected fun register(address: Int, count: Int, format: IOFormat): ModbusRegistryKey.HoldingRange = - ModbusRegistryKey.HoldingRange(address, count, format) + protected fun inputByOffset(offset: Int, description: String = ""): ModbusRegistryKey.InputRegister = + register(ModbusRegistryKey.InputRegister(20000 + offset), description) - protected fun registerByOffset(offset: Int): ModbusRegistryKey.HoldingRegister = - ModbusRegistryKey.HoldingRegister(30000 + offset) + protected fun inputByOffset( + offset: Int, + count: Int, + reader: IOReader, + description: String = "", + ): ModbusRegistryKey.InputRange = + register(ModbusRegistryKey.InputRange(20000 + offset, count, reader), description) - protected fun registerByOffset(offset: Int, count: Int, format: IOFormat): ModbusRegistryKey.HoldingRange = - ModbusRegistryKey.HoldingRange(offset + 30000, count, format) + protected fun register(address: Int, description: String = ""): ModbusRegistryKey.HoldingRegister = + register(ModbusRegistryKey.HoldingRegister(address), description) + + protected fun register( + address: Int, + count: Int, + format: IOFormat, + description: String = "", + ): ModbusRegistryKey.HoldingRange = + register(ModbusRegistryKey.HoldingRange(address, count, format), description) + + protected fun registerByOffset(offset: Int, description: String = ""): ModbusRegistryKey.HoldingRegister = + register(ModbusRegistryKey.HoldingRegister(30000 + offset), description) + + protected fun registerByOffset( + offset: Int, + count: Int, + format: IOFormat, + description: String = "", + ): ModbusRegistryKey.HoldingRange = + register(ModbusRegistryKey.HoldingRange(offset + 30000, count, format), description) + + public companion object { + public fun validate(map: ModbusRegistryMap) { + map.entries.keys.sortedBy { it.address }.zipWithNext().forEach { (l, r) -> + if (l.address + l.count > r.address) error("Key $l overlaps with key $r") + } + } + + public fun print(map: ModbusRegistryMap, to: Appendable = System.out) { + map.entries.entries.sortedBy { it.key.address }.forEach { (key, description) -> + val typeString = when (key) { + is ModbusRegistryKey.Coil -> "Coil" + is ModbusRegistryKey.DiscreteInput -> "Discrete" + is ModbusRegistryKey.HoldingRange<*>, is ModbusRegistryKey.HoldingRegister -> "Register" + is ModbusRegistryKey.InputRange<*>, is ModbusRegistryKey.InputRegister -> "Input" + } + val rangeString = if (key.count == 1) { + key.address.toString() + } else { + "${key.address} - ${key.address + key.count}" + } + to.appendLine("${typeString}\t$rangeString\t$description") + } + } + } } + + 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 e9b4c69..eedafe7 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 @@ -8,6 +8,7 @@ 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 +import kotlin.test.Ignore class OpcUaClientTest { class DemoOpcUaDevice(config: MiloConfiguration) : OpcUaDeviceBySpec(DemoOpcUaDevice, config) { @@ -37,6 +38,7 @@ class OpcUaClientTest { @OptIn(ExperimentalCoroutinesApi::class) @Test + @Ignore fun testReadDouble() = runTest { DemoOpcUaDevice.build().use{ println(it.read(DemoOpcUaDevice.randomDouble)) From 2a966d8cb309b158f6202f1b3ac0f8dbd82d4687 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 24 May 2023 09:37:56 +0300 Subject: [PATCH 07/43] Minor refactor of property listeners --- .../controls/manager/DeviceManager.kt | 14 ++++++----- .../controls/spec/DevicePropertySpec.kt | 23 +++++++++++++------ controls-pi/build.gradle.kts | 8 +++---- .../space/kscience/controls/pi/PiPlugin.kt | 3 +++ 4 files changed, 31 insertions(+), 17 deletions(-) diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/manager/DeviceManager.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/manager/DeviceManager.kt index 871f236..cc043c7 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/manager/DeviceManager.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/manager/DeviceManager.kt @@ -37,12 +37,7 @@ public class DeviceManager : AbstractPlugin(), DeviceHub { } } - -/** - * Register and start a device built by [factory] with current [Context] and [meta]. - */ -public fun DeviceManager.install(name: String, factory: Factory, meta: Meta = Meta.EMPTY): D { - val device = factory(meta, context) +public fun DeviceManager.install(name: String, device: D): D { registerDevice(NameToken(name), device) device.launch { device.open() @@ -50,6 +45,13 @@ public fun DeviceManager.install(name: String, factory: Factory, return device } + +/** + * Register and start a device built by [factory] with current [Context] and [meta]. + */ +public fun DeviceManager.install(name: String, factory: Factory, meta: Meta = Meta.EMPTY): D = + install(name, factory(meta, context)) + /** * A delegate that initializes device on the first use */ 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 078a43d..0e72fca 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 @@ -2,10 +2,7 @@ package space.kscience.controls.spec import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import space.kscience.controls.api.ActionDescriptor import space.kscience.controls.api.Device @@ -105,19 +102,31 @@ public operator fun D.set(propertySpec: WritableDevicePropertySp write(propertySpec, value) } +/** + * A type safe flow of property changes for given property + */ +public fun D.propertyFlow(spec: DevicePropertySpec): Flow = messageFlow + .filterIsInstance() + .filter { it.property == spec.name } + .mapNotNull { spec.converter.metaToObject(it.value) } + /** * A type safe property change listener. Uses the device [CoroutineScope]. */ -public fun Device.onPropertyChange( +public fun D.onPropertyChange( spec: DevicePropertySpec, - callback: suspend PropertyChangedMessage.(T?) -> Unit, + callback: suspend PropertyChangedMessage.(T) -> Unit, ): Job = messageFlow .filterIsInstance() .filter { it.property == spec.name } .onEach { change -> - change.callback(spec.converter.metaToObject(change.value)) + val newValue = spec.converter.metaToObject(change.value) + if (newValue != null) { + change.callback(newValue) + } }.launchIn(this) + /** * Reset the logical state of a property */ diff --git a/controls-pi/build.gradle.kts b/controls-pi/build.gradle.kts index 3fa0dbc..b269c85 100644 --- a/controls-pi/build.gradle.kts +++ b/controls-pi/build.gradle.kts @@ -5,8 +5,8 @@ plugins { dependencies{ api(project(":controls-core")) - implementation("com.pi4j:pi4j-ktx:2.4.0") // Kotlin DSL - implementation("com.pi4j:pi4j-core:2.3.0") - implementation("com.pi4j:pi4j-plugin-raspberrypi:2.3.0") - implementation("com.pi4j:pi4j-plugin-pigpio:2.3.0") + api("com.pi4j:pi4j-ktx:2.4.0") // Kotlin DSL + api("com.pi4j:pi4j-core:2.3.0") + api("com.pi4j:pi4j-plugin-raspberrypi:2.3.0") + api("com.pi4j:pi4j-plugin-pigpio:2.3.0") } \ No newline at end of file diff --git a/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiPlugin.kt b/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiPlugin.kt index e3d6a0a..547a142 100644 --- a/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiPlugin.kt +++ b/controls-pi/src/main/kotlin/space/kscience/controls/pi/PiPlugin.kt @@ -1,5 +1,6 @@ package space.kscience.controls.pi +import space.kscience.controls.ports.Ports import space.kscience.dataforge.context.AbstractPlugin import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.PluginFactory @@ -7,6 +8,8 @@ import space.kscience.dataforge.context.PluginTag import space.kscience.dataforge.meta.Meta public class PiPlugin : AbstractPlugin() { + public val ports: Ports by require(Ports) + override val tag: PluginTag get() = Companion.tag public companion object : PluginFactory { From b7be570271769e93bd05cddeefd5a358e820b376 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 24 May 2023 09:56:02 +0300 Subject: [PATCH 08/43] Fix property listener --- .../pimotionmaster/fxDeviceProperties.kt | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) 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 e8b4e68..93f9f7c 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 @@ -12,8 +12,8 @@ import tornadofx.* /** * Bind a FX property to a device property with a given [spec] */ -fun Device.fxProperty( - spec: DevicePropertySpec +fun D.fxProperty( + spec: DevicePropertySpec, ): ReadOnlyProperty = object : ObjectPropertyBase() { override fun getBean(): Any = this override fun getName(): String = spec.name @@ -21,16 +21,12 @@ fun Device.fxProperty( init { //Read incoming changes onPropertyChange(spec) { - if (it != null) { - runLater { - try { - set(it) - } catch (ex: Throwable) { - logger.info { "Failed to set property $name to $it" } - } + runLater { + try { + set(it) + } catch (ex: Throwable) { + logger.info { "Failed to set property $name to $it" } } - } else { - invalidated() } } } From 25d7fe0a8e4eb0d7e7ce5563c6e5d9bb474c2cf1 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 25 May 2023 09:51:27 +0300 Subject: [PATCH 09/43] Update actions signatures to avoid unnecessary nulls --- .../kscience/controls/spec/DeviceBase.kt | 6 +++--- .../controls/spec/DevicePropertySpec.kt | 9 ++++++--- .../kscience/controls/spec/DeviceSpec.kt | 20 ++++++++++++------- .../kscience/controls/demo/DemoDevice.kt | 3 +-- 4 files changed, 23 insertions(+), 15 deletions(-) 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 0f5436b..034485f 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 @@ -25,9 +25,9 @@ private suspend fun DevicePropertySpec.readMeta(device: D) private suspend fun DeviceActionSpec.executeWithMeta( device: D, - item: Meta?, + item: Meta, ): Meta? { - val arg = item?.let { inputConverter.metaToObject(item) } + val arg: I = inputConverter.metaToObject(item) ?: error("Failed to convert $item with $inputConverter") val res = execute(device, arg) return res?.let { outputConverter.objectToMeta(res) } } @@ -146,7 +146,7 @@ public abstract class DeviceBase( override suspend fun execute(actionName: String, argument: Meta?): Meta? { val spec = actions[actionName] ?: error("Action with name $actionName not found") - return spec.executeWithMeta(self, argument) + return spec.executeWithMeta(self, argument ?: 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 0e72fca..ce83edc 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 @@ -66,7 +66,7 @@ public interface DeviceActionSpec { /** * Execute action on a device */ - public suspend fun execute(device: D, input: I?): O? + public suspend fun execute(device: D, input: I): O } /** @@ -137,5 +137,8 @@ public suspend fun D.invalidate(propertySpec: DevicePropertySpec D.execute(actionSpec: DeviceActionSpec, input: I? = null): O? = - actionSpec.execute(this, input) \ No newline at end of file +public suspend fun D.execute(actionSpec: DeviceActionSpec, input: I): O = + actionSpec.execute(this, input) + +public suspend fun D.execute(actionSpec: DeviceActionSpec): O = + actionSpec.execute(this, Unit) \ No newline at end of file diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt index eb5c978..ff7dddd 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt @@ -12,6 +12,13 @@ import kotlin.reflect.KMutableProperty1 import kotlin.reflect.KProperty import kotlin.reflect.KProperty1 +public object UnitMetaConverter: MetaConverter{ + override fun metaToObject(meta: Meta): Unit = Unit + + override fun objectToMeta(obj: Unit): Meta = Meta.EMPTY +} + +public val MetaConverter.Companion.unit: MetaConverter get() = UnitMetaConverter @OptIn(InternalDeviceAPI::class) public abstract class DeviceSpec { @@ -165,7 +172,7 @@ public abstract class DeviceSpec { outputConverter: MetaConverter, descriptorBuilder: ActionDescriptor.() -> Unit = {}, name: String? = null, - execute: suspend D.(I?) -> O?, + execute: suspend D.(I) -> O, ): PropertyDelegateProvider, ReadOnlyProperty, DeviceActionSpec>> = PropertyDelegateProvider { _: DeviceSpec, property -> val actionName = name ?: property.name @@ -175,7 +182,7 @@ public abstract class DeviceSpec { override val inputConverter: MetaConverter = inputConverter override val outputConverter: MetaConverter = outputConverter - override suspend fun execute(device: D, input: I?): O? = withContext(device.coroutineContext) { + override suspend fun execute(device: D, input: I): O = withContext(device.coroutineContext) { device.execute(input) } } @@ -191,7 +198,7 @@ public abstract class DeviceSpec { public fun metaAction( descriptorBuilder: ActionDescriptor.() -> Unit = {}, name: String? = null, - execute: suspend D.(Meta?) -> Meta?, + execute: suspend D.(Meta) -> Meta, ): PropertyDelegateProvider, ReadOnlyProperty, DeviceActionSpec>> = action( MetaConverter.Companion.meta, @@ -209,15 +216,14 @@ public abstract class DeviceSpec { descriptorBuilder: ActionDescriptor.() -> Unit = {}, name: String? = null, execute: suspend D.() -> Unit, - ): PropertyDelegateProvider, ReadOnlyProperty, DeviceActionSpec>> = + ): PropertyDelegateProvider, ReadOnlyProperty, DeviceActionSpec>> = action( - MetaConverter.Companion.meta, - MetaConverter.Companion.meta, + MetaConverter.Companion.unit, + MetaConverter.Companion.unit, descriptorBuilder, name ) { execute() - null } } 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 37b7b4b..dd65b78 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 @@ -68,11 +68,10 @@ class DemoDevice(context: Context, meta: Meta) : DeviceBySpec(Compa } - val resetScale by action(MetaConverter.meta, MetaConverter.meta) { + val resetScale by unitAction { write(timeScale, 5000.0) write(sinScale, 1.0) write(cosScale, 1.0) - null } override suspend fun IDemoDevice.onOpen() { From 7b792afd7a864c575d2e25004e53d48cdc8cf947 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 25 May 2023 10:07:51 +0300 Subject: [PATCH 10/43] Fix nullability in actions --- .../pimotionmaster/PiMotionMasterDevice.kt | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt index 3a36131..4eff6c4 100644 --- a/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt +++ b/demo/motors/src/main/kotlin/ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice.kt @@ -162,7 +162,7 @@ class PiMotionMasterDevice( send("STP") } - val connect by metaAction(descriptorBuilder = { + val connect by action(MetaConverter.meta, MetaConverter.unit, descriptorBuilder = { info = "Connect to specific port and initialize axis" }) { portSpec -> //Clear current actions if present @@ -171,36 +171,32 @@ class PiMotionMasterDevice( } //Update port //address = portSpec.node - port = portFactory(portSpec ?: Meta.EMPTY, context) + port = portFactory(portSpec, context) updateLogical(connected, true) // connector.open() //Initialize axes - if (portSpec != null) { - val idn = read(identity) - failIfError { "Can't connect to $portSpec. Error code: $it" } - logger.info { "Connected to $idn on $portSpec" } - val ids = request("SAI?").map { it.trim() } - if (ids != axes.keys.toList()) { - //re-define axes if needed - axes = ids.associateWith { Axis(this, it) } - } - Meta(ids.map { it.asValue() }.asValue()) - execute(initialize) - failIfError() + val idn = read(identity) + failIfError { "Can't connect to $portSpec. Error code: $it" } + logger.info { "Connected to $idn on $portSpec" } + val ids = request("SAI?").map { it.trim() } + if (ids != axes.keys.toList()) { + //re-define axes if needed + axes = ids.associateWith { Axis(this, it) } } - null + Meta(ids.map { it.asValue() }.asValue()) + execute(initialize) + failIfError() } - val disconnect by metaAction({ + val disconnect by unitAction({ info = "Disconnect the program from the device if it is connected" }) { - port?.let{ + port?.let { execute(stop) it.close() } port = null updateLogical(connected, false) - null } @@ -212,7 +208,7 @@ class PiMotionMasterDevice( class Axis( val mm: PiMotionMasterDevice, - val axisId: String + val axisId: String, ) : DeviceBySpec(Axis, mm.context) { /** @@ -244,7 +240,7 @@ class PiMotionMasterDevice( private fun axisBooleanProperty( command: String, - descriptorBuilder: PropertyDescriptor.() -> Unit = {} + descriptorBuilder: PropertyDescriptor.() -> Unit = {}, ) = booleanProperty( read = { readAxisBoolean("$command?") @@ -257,7 +253,7 @@ class PiMotionMasterDevice( private fun axisNumberProperty( command: String, - descriptorBuilder: PropertyDescriptor.() -> Unit = {} + descriptorBuilder: PropertyDescriptor.() -> Unit = {}, ) = doubleProperty( read = { mm.requestAndParse("$command?", axisId)[axisId]?.toDoubleOrNull() @@ -334,11 +330,11 @@ class PiMotionMasterDevice( info = "Velocity value for closed-loop operation" } - val move by metaAction { - val target = it.double ?: it?.get("target").double ?: error("Unacceptable target value $it") + val move by action(MetaConverter.meta, MetaConverter.unit) { + val target = it.double ?: it["target"].double ?: error("Unacceptable target value $it") write(closedLoop, true) //optionally set velocity - it?.get("velocity").double?.let { v -> + it["velocity"].double?.let { v -> write(velocity, v) } write(targetPosition, target) @@ -347,7 +343,6 @@ class PiMotionMasterDevice( read(position) delay(200) } - null } } From ab1a478867815941c165667488cee32944330afb Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 27 May 2023 19:49:25 +0300 Subject: [PATCH 11/43] Add publishing --- controls-ktor-tcp/build.gradle.kts | 1 + controls-modbus/build.gradle.kts | 1 + controls-opcua/build.gradle.kts | 1 + gradle.properties | 5 ++++- 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/controls-ktor-tcp/build.gradle.kts b/controls-ktor-tcp/build.gradle.kts index 2089b19..93e376c 100644 --- a/controls-ktor-tcp/build.gradle.kts +++ b/controls-ktor-tcp/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("space.kscience.gradle.jvm") + `maven-publish` } val ktorVersion: String by rootProject.extra diff --git a/controls-modbus/build.gradle.kts b/controls-modbus/build.gradle.kts index 8734460..341d201 100644 --- a/controls-modbus/build.gradle.kts +++ b/controls-modbus/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("space.kscience.gradle.jvm") + `maven-publish` } description = """ diff --git a/controls-opcua/build.gradle.kts b/controls-opcua/build.gradle.kts index 02510e2..b0530c7 100644 --- a/controls-opcua/build.gradle.kts +++ b/controls-opcua/build.gradle.kts @@ -1,5 +1,6 @@ plugins { id("space.kscience.gradle.jvm") + `maven-publish` } val ktorVersion: String by rootProject.extra diff --git a/gradle.properties b/gradle.properties index 3ba1c64..5c5294c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,4 +7,7 @@ org.gradle.parallel=true publishing.github=false publishing.sonatype=false -toolsVersion=0.14.6-kotlin-1.8.20 \ No newline at end of file +org.gradle.configureondemand=true +org.gradle.jvmargs=-Xmx4096m + +toolsVersion=0.14.8-kotlin-1.8.20 \ No newline at end of file From ced42779be738ea38d2882635c17412278d4b74f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 27 May 2023 21:36:46 +0300 Subject: [PATCH 12/43] Fix UDP connection --- .../jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt index 0a9a0f0..d0b9e1f 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt @@ -114,7 +114,7 @@ public object UdpPort : PortFactory { coroutineContext: CoroutineContext = context.coroutineContext, ): ChannelPort = ChannelPort(context,coroutineContext){ DatagramChannel.open().apply { - bind(InetSocketAddress(host, port)) + connect(InetSocketAddress(host, port)) configureBlocking(false) } } From 22359a557068094c2e54a6de56616035bad8756d Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 27 May 2023 22:44:30 +0300 Subject: [PATCH 13/43] Remove non-blocking mode for ports to avoid CPU clogging. --- .../kotlin/space/kscience/controls/ports/Port.kt | 12 +++++------- .../space/kscience/controls/ports/ChannelPort.kt | 5 +---- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt index db19817..1f07251 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/ports/Port.kt @@ -18,10 +18,10 @@ public interface Port : ContextAware, Socket * A specialized factory for [Port] */ @Type(PortFactory.TYPE) -public interface PortFactory: Factory{ +public interface PortFactory : Factory { public val type: String - public companion object{ + public companion object { public const val TYPE: String = "controls.port" } } @@ -53,11 +53,9 @@ public abstract class AbstractPort( /** * Internal method to receive data synchronously */ - protected fun receive(data: ByteArray) { - scope.launch { - logger.debug { "${this@AbstractPort} RECEIVED: ${data.decodeToString()}" } - incoming.send(data) - } + protected suspend fun receive(data: ByteArray) { + logger.debug { "${this@AbstractPort} RECEIVED: ${data.decodeToString()}" } + incoming.send(data) } private val sendJob = scope.launch { diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt index d0b9e1f..6ddc1ab 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt @@ -87,9 +87,7 @@ public object TcpPort : PortFactory { port: Int, coroutineContext: CoroutineContext = context.coroutineContext, ): ChannelPort = ChannelPort(context,coroutineContext){ - SocketChannel.open(InetSocketAddress(host, port)).apply { - configureBlocking(false) - } + SocketChannel.open(InetSocketAddress(host, port)) } override fun build(context: Context, meta: Meta): ChannelPort { @@ -115,7 +113,6 @@ public object UdpPort : PortFactory { ): ChannelPort = ChannelPort(context,coroutineContext){ DatagramChannel.open().apply { connect(InetSocketAddress(host, port)) - configureBlocking(false) } } From 9c39f44897c9d02c733ce013d0815c2b5975e7a8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 27 May 2023 22:55:21 +0300 Subject: [PATCH 14/43] fix async port receive --- .../main/kotlin/space/kscience/controls/serial/SerialPort.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt index 05d4b64..54b7ac8 100644 --- a/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt +++ b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt @@ -2,6 +2,7 @@ package space.kscience.controls.serial import jssc.SerialPort.* import jssc.SerialPortEventListener +import kotlinx.coroutines.launch import space.kscience.controls.ports.AbstractPort import space.kscience.controls.ports.Port import space.kscience.controls.ports.PortFactory @@ -27,7 +28,7 @@ public class SerialPort private constructor( if (event.isRXCHAR) { val chars = event.eventValue val bytes = jssc.readBytes(chars) - receive(bytes) + scope.launch { receive(bytes) } } } From 2191bb7a7783ba833d880c5012ea5f2eca01d5eb Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 30 May 2023 15:02:09 +0300 Subject: [PATCH 15/43] Update visualization --- .../space/kscience/controls/api/DeviceHub.kt | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt index 9565950..8bd7452 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt @@ -14,26 +14,24 @@ public interface DeviceHub : Provider { override val defaultChainTarget: String get() = Device.DEVICE_TARGET - override fun content(target: String): Map { - if (target == Device.DEVICE_TARGET) { - return buildMap { - fun putAll(prefix: Name, hub: DeviceHub) { - hub.devices.forEach { - put(prefix + it.key, it.value) - } - } - - devices.forEach { - val name = it.key.asName() - put(name, it.value) - (it.value as? DeviceHub)?.let { hub -> - putAll(name, hub) - } + override fun content(target: String): Map = if (target == Device.DEVICE_TARGET) { + buildMap { + fun putAll(prefix: Name, hub: DeviceHub) { + hub.devices.forEach { + put(prefix + it.key, it.value) + } + } + + devices.forEach { + val name = it.key.asName() + put(name, it.value) + (it.value as? DeviceHub)?.let { hub -> + putAll(name, hub) } } - } else { - throw IllegalArgumentException("Target $target is not supported for $this") } + } else { + emptyMap() } public companion object From 76cec0469ff8de4b4cc35136e38d097e483bbddb Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Tue, 30 May 2023 20:23:43 +0300 Subject: [PATCH 16/43] add useProperty subscription --- .../controls/spec/DevicePropertySpec.kt | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) 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 ce83edc..cf8c741 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 @@ -126,6 +126,25 @@ public fun D.onPropertyChange( } }.launchIn(this) +/** + * Call [callback] on initial property value and each value change + */ +public fun D.useProperty( + spec: DevicePropertySpec, + callback: suspend (T) -> Unit, +): Job = launch { + callback(read(spec)) + messageFlow + .filterIsInstance() + .filter { it.property == spec.name } + .collect { change -> + val newValue = spec.converter.metaToObject(change.value) + if (newValue != null) { + callback(newValue) + } + } +} + /** * Reset the logical state of a property From 2aa26ea802969c0f303b6dd1a2782f01cc498d9d Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 4 Jun 2023 11:23:26 +0300 Subject: [PATCH 17/43] Update UDP ports design. Rename ktor ports module. --- .../kscience/controls/ports/ChannelPort.kt | 33 ++++--- .../README.md | 0 .../build.gradle.kts | 0 .../controls/ports/KtorPortsPlugin.kt | 13 +-- .../kscience/controls/ports/KtorTcpPort.kt | 0 .../kscience/controls/ports/KtorUdpPort.kt | 87 +++++++++++++++++++ controls-server/build.gradle.kts | 2 +- demo/mks-pdr900/build.gradle.kts | 2 +- demo/motors/build.gradle.kts | 2 +- settings.gradle.kts | 2 +- 10 files changed, 118 insertions(+), 23 deletions(-) rename {controls-ktor-tcp => controls-ports-ktor}/README.md (100%) rename {controls-ktor-tcp => controls-ports-ktor}/build.gradle.kts (100%) rename controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPortPlugin.kt => controls-ports-ktor/src/main/kotlin/space/kscience/controls/ports/KtorPortsPlugin.kt (51%) rename {controls-ktor-tcp => controls-ports-ktor}/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt (100%) create mode 100644 controls-ports-ktor/src/main/kotlin/space/kscience/controls/ports/KtorUdpPort.kt diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt index 6ddc1ab..04d1eb9 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt @@ -4,10 +4,7 @@ import kotlinx.coroutines.* import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.error import space.kscience.dataforge.context.logger -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.get -import space.kscience.dataforge.meta.int -import space.kscience.dataforge.meta.string +import space.kscience.dataforge.meta.* import java.net.InetSocketAddress import java.nio.ByteBuffer import java.nio.channels.ByteChannel @@ -26,7 +23,7 @@ public fun ByteBuffer.toArray(limit: Int = limit()): ByteArray { /** * A port based on nio [ByteChannel] */ -public class ChannelPort ( +public class ChannelPort( context: Context, coroutineContext: CoroutineContext = context.coroutineContext, channelBuilder: suspend () -> ByteChannel, @@ -86,7 +83,7 @@ public object TcpPort : PortFactory { host: String, port: Int, coroutineContext: CoroutineContext = context.coroutineContext, - ): ChannelPort = ChannelPort(context,coroutineContext){ + ): ChannelPort = ChannelPort(context, coroutineContext) { SocketChannel.open(InetSocketAddress(host, port)) } @@ -105,20 +102,30 @@ public object UdpPort : PortFactory { override val type: String = "udp" + /** + * Connect a datagram channel to a remote host/port. If [localPort] is provided, it is used to bind local port for receiving messages. + */ public fun open( context: Context, - host: String, - port: Int, + remoteHost: String, + remotePort: Int, + localPort: Int? = null, + localHost: String = "localhost", coroutineContext: CoroutineContext = context.coroutineContext, - ): ChannelPort = ChannelPort(context,coroutineContext){ + ): ChannelPort = ChannelPort(context, coroutineContext) { DatagramChannel.open().apply { - connect(InetSocketAddress(host, port)) + //bind the channel to a local port to receive messages + localPort?.let { bind(InetSocketAddress(localHost, localPort)) } + //connect to remote port to send messages + connect(InetSocketAddress(remoteHost, remotePort)) } } override fun build(context: Context, meta: Meta): ChannelPort { - val host = meta["host"].string ?: "localhost" - val port = meta["port"].int ?: error("Port value for UDP port is not defined in $meta") - return open(context, host, port) + val remoteHost by meta.string { error("Remote host is not specified") } + val remotePort by meta.number { error("Remote port is not specified") } + val localHost: String? by meta.string() + val localPort: Int? by meta.int() + return open(context, remoteHost, remotePort.toInt(), localPort, localHost ?: "localhost") } } \ No newline at end of file diff --git a/controls-ktor-tcp/README.md b/controls-ports-ktor/README.md similarity index 100% rename from controls-ktor-tcp/README.md rename to controls-ports-ktor/README.md diff --git a/controls-ktor-tcp/build.gradle.kts b/controls-ports-ktor/build.gradle.kts similarity index 100% rename from controls-ktor-tcp/build.gradle.kts rename to controls-ports-ktor/build.gradle.kts diff --git a/controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPortPlugin.kt b/controls-ports-ktor/src/main/kotlin/space/kscience/controls/ports/KtorPortsPlugin.kt similarity index 51% rename from controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPortPlugin.kt rename to controls-ports-ktor/src/main/kotlin/space/kscience/controls/ports/KtorPortsPlugin.kt index 079f457..9c6dbba 100644 --- a/controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPortPlugin.kt +++ b/controls-ports-ktor/src/main/kotlin/space/kscience/controls/ports/KtorPortsPlugin.kt @@ -6,21 +6,22 @@ import space.kscience.dataforge.context.PluginFactory import space.kscience.dataforge.context.PluginTag import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.names.Name +import space.kscience.dataforge.names.asName -public class KtorTcpPortPlugin : AbstractPlugin() { +public class KtorPortsPlugin : AbstractPlugin() { override val tag: PluginTag get() = Companion.tag - override fun content(target: String): Map = when(target){ - PortFactory.TYPE -> mapOf(Name.EMPTY to KtorTcpPort) + override fun content(target: String): Map = when (target) { + PortFactory.TYPE -> mapOf("tcp".asName() to KtorTcpPort, "udp".asName() to KtorUdpPort) else -> emptyMap() } - public companion object : PluginFactory { + public companion object : PluginFactory { - override val tag: PluginTag = PluginTag("controls.ports.serial", group = PluginTag.DATAFORGE_GROUP) + override val tag: PluginTag = PluginTag("controls.ports.ktor", group = PluginTag.DATAFORGE_GROUP) - override fun build(context: Context, meta: Meta): KtorTcpPortPlugin = KtorTcpPortPlugin() + override fun build(context: Context, meta: Meta): KtorPortsPlugin = KtorPortsPlugin() } diff --git a/controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt b/controls-ports-ktor/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt similarity index 100% rename from controls-ktor-tcp/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt rename to controls-ports-ktor/src/main/kotlin/space/kscience/controls/ports/KtorTcpPort.kt diff --git a/controls-ports-ktor/src/main/kotlin/space/kscience/controls/ports/KtorUdpPort.kt b/controls-ports-ktor/src/main/kotlin/space/kscience/controls/ports/KtorUdpPort.kt new file mode 100644 index 0000000..8b8446c --- /dev/null +++ b/controls-ports-ktor/src/main/kotlin/space/kscience/controls/ports/KtorUdpPort.kt @@ -0,0 +1,87 @@ +package space.kscience.controls.ports + +import io.ktor.network.selector.ActorSelectorManager +import io.ktor.network.sockets.InetSocketAddress +import io.ktor.network.sockets.aSocket +import io.ktor.network.sockets.openReadChannel +import io.ktor.network.sockets.openWriteChannel +import io.ktor.utils.io.consumeEachBufferRange +import io.ktor.utils.io.core.Closeable +import io.ktor.utils.io.writeAvailable +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.int +import space.kscience.dataforge.meta.number +import space.kscience.dataforge.meta.string +import kotlin.coroutines.CoroutineContext + +public class KtorUdpPort internal constructor( + context: Context, + public val remoteHost: String, + public val remotePort: Int, + public val localPort: Int? = null, + public val localHost: String = "localhost", + coroutineContext: CoroutineContext = context.coroutineContext, +) : AbstractPort(context, coroutineContext), Closeable { + + override fun toString(): String = "port[udp:$remoteHost:$remotePort]" + + private val futureSocket = scope.async { + aSocket(ActorSelectorManager(Dispatchers.IO)).udp().connect( + remoteAddress = InetSocketAddress(remoteHost, remotePort), + localAddress = localPort?.let { InetSocketAddress(localHost, localPort) } + ) + } + + private val writeChannel = scope.async { + futureSocket.await().openWriteChannel(true) + } + + private val listenerJob = scope.launch { + val input = futureSocket.await().openReadChannel() + input.consumeEachBufferRange { buffer, _ -> + val array = ByteArray(buffer.remaining()) + buffer.get(array) + receive(array) + isActive + } + } + + override suspend fun write(data: ByteArray) { + writeChannel.await().writeAvailable(data) + } + + override fun close() { + listenerJob.cancel() + futureSocket.cancel() + super.close() + } + + public companion object : PortFactory { + + override val type: String = "udp" + + public fun open( + context: Context, + remoteHost: String, + remotePort: Int, + localPort: Int? = null, + localHost: String = "localhost", + coroutineContext: CoroutineContext = context.coroutineContext, + ): KtorUdpPort { + return KtorUdpPort(context, remoteHost, remotePort, localPort, localHost, coroutineContext) + } + + override fun build(context: Context, meta: Meta): Port { + val remoteHost by meta.string { error("Remote host is not specified") } + val remotePort by meta.number { error("Remote port is not specified") } + val localHost: String? by meta.string() + val localPort: Int? by meta.int() + return open(context, remoteHost, remotePort.toInt(), localPort, localHost ?: "localhost") + } + } +} \ No newline at end of file diff --git a/controls-server/build.gradle.kts b/controls-server/build.gradle.kts index d661459..2bf9b01 100644 --- a/controls-server/build.gradle.kts +++ b/controls-server/build.gradle.kts @@ -12,7 +12,7 @@ val ktorVersion: String by rootProject.extra dependencies { implementation(projects.controlsCore) - implementation(projects.controlsKtorTcp) + implementation(projects.controlsPortsKtor) implementation(projects.magix.magixServer) implementation("io.ktor:ktor-server-cio:$ktorVersion") implementation("io.ktor:ktor-server-websockets:$ktorVersion") diff --git a/demo/mks-pdr900/build.gradle.kts b/demo/mks-pdr900/build.gradle.kts index bae6263..dfa7543 100644 --- a/demo/mks-pdr900/build.gradle.kts +++ b/demo/mks-pdr900/build.gradle.kts @@ -17,5 +17,5 @@ val ktorVersion: String by rootProject.extra val dataforgeVersion: String by extra dependencies { - implementation(projects.controlsKtorTcp) + implementation(projects.controlsPortsKtor) } diff --git a/demo/motors/build.gradle.kts b/demo/motors/build.gradle.kts index b60ebb9..0acea4a 100644 --- a/demo/motors/build.gradle.kts +++ b/demo/motors/build.gradle.kts @@ -23,7 +23,7 @@ val ktorVersion: String by rootProject.extra val dataforgeVersion: String by extra dependencies { - implementation(project(":controls-ktor-tcp")) + implementation(project(":controls-ports-ktor")) implementation(project(":controls-magix-client")) implementation("no.tornado:tornadofx:1.7.20") } diff --git a/settings.gradle.kts b/settings.gradle.kts index 72a29bd..4fee116 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -41,7 +41,7 @@ dependencyResolutionManagement { include( ":controls-core", - ":controls-ktor-tcp", + ":controls-ports-ktor", ":controls-serial", ":controls-pi", ":controls-server", From 166cc03fe26e8c8bdc64d24e5d3867b11b5df5bb Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 4 Jun 2023 21:18:42 +0300 Subject: [PATCH 18/43] Add modbus client --- .../kscience/controls/spec/DeviceSpec.kt | 21 +-- .../controls/modbus/DeviceProcessImage.kt | 145 ++++++++++++++++++ .../controls/modbus/ModbusRegistryMap.kt | 7 +- 3 files changed, 149 insertions(+), 24 deletions(-) create mode 100644 controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt index ff7dddd..d05bf30 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceSpec.kt @@ -44,25 +44,6 @@ public abstract class DeviceSpec { return deviceProperty } -// public fun registerProperty( -// converter: MetaConverter, -// readOnlyProperty: KProperty1, -// descriptorBuilder: PropertyDescriptor.() -> Unit = {}, -// ): DevicePropertySpec { -// val deviceProperty = object : DevicePropertySpec { -// -// override val descriptor: PropertyDescriptor = PropertyDescriptor(readOnlyProperty.name) -// .apply(descriptorBuilder) -// -// override val converter: MetaConverter = converter -// -// override suspend fun read(device: D): T = withContext(device.coroutineContext) { -// readOnlyProperty.get(device) -// } -// } -// return registerProperty(deviceProperty) -// } - public fun property( converter: MetaConverter, readOnlyProperty: KProperty1, @@ -96,7 +77,7 @@ public abstract class DeviceSpec { val deviceProperty = object : WritableDevicePropertySpec { override val descriptor: PropertyDescriptor = PropertyDescriptor(property.name).apply { - //TODO add type from converter + //TODO add the type from converter writable = true }.apply(descriptorBuilder) diff --git a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt new file mode 100644 index 0000000..581d228 --- /dev/null +++ b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt @@ -0,0 +1,145 @@ +package space.kscience.controls.modbus + +import com.ghgande.j2mod.modbus.procimg.* +import io.ktor.utils.io.core.buildPacket +import io.ktor.utils.io.core.readByteBuffer +import io.ktor.utils.io.core.writeShort +import space.kscience.controls.api.Device +import space.kscience.controls.spec.DevicePropertySpec +import space.kscience.controls.spec.WritableDevicePropertySpec +import space.kscience.controls.spec.set +import space.kscience.controls.spec.useProperty + + +public class DeviceToModbusMapping private constructor( + private val mapping: Map, ModbusRegistryKey>, +) : Map, ModbusRegistryKey> by mapping { + public class Builder { + private val mapping = HashMap, ModbusRegistryKey>() + + public fun bind(propertySpec: DevicePropertySpec, key: ModbusRegistryKey.DiscreteInput) { + mapping[propertySpec] = key + } + + public fun bind(propertySpec: WritableDevicePropertySpec, key: ModbusRegistryKey.Coil) { + mapping[propertySpec] = key + } + + public fun bind(propertySpec: DevicePropertySpec, key: ModbusRegistryKey.InputRegister) { + mapping[propertySpec] = key + } + + public fun bind(propertySpec: WritableDevicePropertySpec, key: ModbusRegistryKey.HoldingRegister) { + mapping[propertySpec] = key + } + + public fun bind(propertySpec: DevicePropertySpec, key: ModbusRegistryKey.InputRange) { + mapping[propertySpec] = key + } + + public fun bind(propertySpec: WritableDevicePropertySpec, key: ModbusRegistryKey.HoldingRange) { + mapping[propertySpec] = key + } + + public fun build(): DeviceToModbusMapping = DeviceToModbusMapping(mapping) + } +} + +public fun DeviceToModbusMapping(block: DeviceToModbusMapping.Builder.()->Unit): DeviceToModbusMapping = + DeviceToModbusMapping.Builder().apply(block).build() + +@Suppress("UNCHECKED_CAST") +public fun D.toProcessImage(mapping: DeviceToModbusMapping): ProcessImage { + val image = SimpleProcessImage() + mapping.forEach { (spec, key) -> + when (key) { + is ModbusRegistryKey.Coil -> { + spec as WritableDevicePropertySpec + val coil = ObservableDigitalOut() + coil.addObserver { _, _ -> + set(spec, coil.isSet) + } + image.setDigitalOut(key.address, coil) + useProperty(spec) { value -> + coil.set(value) + } + } + + is ModbusRegistryKey.DiscreteInput -> { + spec as DevicePropertySpec + val input = SimpleDigitalIn() + image.setDigitalIn(key.address, input) + useProperty(spec) { value -> + input.set(value) + } + } + + is ModbusRegistryKey.HoldingRegister -> { + spec as WritableDevicePropertySpec + val register = ObservableRegister() + register.addObserver { _, _ -> + set(spec, register.toShort()) + } + image.setRegister(key.address, register) + useProperty(spec) { value -> + register.setValue(value) + } + } + + is ModbusRegistryKey.InputRegister -> { + spec as DevicePropertySpec + val input = SimpleInputRegister() + image.setRegister(key.address, input) + useProperty(spec) { value -> + input.setValue(value) + } + } + + is ModbusRegistryKey.HoldingRange<*> -> { + spec as WritableDevicePropertySpec + key as ModbusRegistryKey.HoldingRange + val registers = List(key.count) { + ObservableRegister() + } + registers.forEachIndexed { index, register -> + register.addObserver { _, _ -> + val packet = buildPacket { + registers.forEach { value -> + writeShort(value.toShort()) + } + } + set(spec, key.format.readObject(packet)) + } + image.setRegister(key.address + index, register) + } + + useProperty(spec) { value -> + val packet = buildPacket { + key.format.writeObject(this, value) + }.readByteBuffer() + registers.forEachIndexed { index, observableRegister -> + observableRegister.setValue(packet.getShort(index * 2)) + } + } + } + + is ModbusRegistryKey.InputRange<*> -> { + spec as DevicePropertySpec + key as ModbusRegistryKey.InputRange + val registers = List(key.count) { + SimpleInputRegister() + } + + useProperty(spec) { value -> + val packet = buildPacket { + key.format.writeObject(this, value) + }.readByteBuffer() + registers.forEachIndexed { index, register -> + register.setValue(packet.getShort(index * 2)) + } + } + } + } + } + return image +} \ No newline at end of file diff --git a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt index d367397..80e997a 100644 --- a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt +++ b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt @@ -1,7 +1,6 @@ package space.kscience.controls.modbus import space.kscience.dataforge.io.IOFormat -import space.kscience.dataforge.io.IOReader public sealed class ModbusRegistryKey { @@ -38,7 +37,7 @@ public sealed class ModbusRegistryKey { public data class InputRange( override val address: Int, override val count: Int, - public val format: IOReader, + public val format: IOFormat, ) : ModbusRegistryKey() { public val endAddress: Int get() = address + count @@ -97,7 +96,7 @@ public abstract class ModbusRegistryMap { protected fun input( address: Int, count: Int, - reader: IOReader, + reader: IOFormat, description: String = "", ): ModbusRegistryKey.InputRange = register(ModbusRegistryKey.InputRange(address, count, reader), description) @@ -108,7 +107,7 @@ public abstract class ModbusRegistryMap { protected fun inputByOffset( offset: Int, count: Int, - reader: IOReader, + reader: IOFormat, description: String = "", ): ModbusRegistryKey.InputRange = register(ModbusRegistryKey.InputRange(20000 + offset, count, reader), description) From bfeeed00d56b9727b9b550f6e3502c414a3d2dc4 Mon Sep 17 00:00:00 2001 From: darksnake Date: Mon, 5 Jun 2023 17:12:01 +0300 Subject: [PATCH 19/43] process images cleanup --- .../kotlin/space/kscience/controls/ports/ChannelPort.kt | 2 ++ .../space/kscience/controls/modbus/DeviceProcessImage.kt | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt index 04d1eb9..d7983f2 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt @@ -3,6 +3,7 @@ package space.kscience.controls.ports import kotlinx.coroutines.* import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.error +import space.kscience.dataforge.context.info import space.kscience.dataforge.context.logger import space.kscience.dataforge.meta.* import java.net.InetSocketAddress @@ -118,6 +119,7 @@ public object UdpPort : PortFactory { localPort?.let { bind(InetSocketAddress(localHost, localPort)) } //connect to remote port to send messages connect(InetSocketAddress(remoteHost, remotePort)) + context.logger.info { "Connected to UDP $remotePort on $remoteHost" } } } diff --git a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt index 581d228..47acb4a 100644 --- a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt +++ b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt @@ -45,7 +45,7 @@ public class DeviceToModbusMapping private constructor( } } -public fun DeviceToModbusMapping(block: DeviceToModbusMapping.Builder.()->Unit): DeviceToModbusMapping = +public inline fun DeviceToModbusMapping(block: DeviceToModbusMapping.Builder.()->Unit): DeviceToModbusMapping = DeviceToModbusMapping.Builder().apply(block).build() @Suppress("UNCHECKED_CAST") @@ -142,4 +142,7 @@ public fun D.toProcessImage(mapping: DeviceToModbusMapping): Pro } } return image -} \ No newline at end of file +} + +public inline fun D.toProcessImage(block: DeviceToModbusMapping.Builder.()->Unit): ProcessImage = + toProcessImage(DeviceToModbusMapping(block)) \ No newline at end of file From 33e1aafa012f9dc4fcdad79a4b888bf3378e7a3b Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 8 Jun 2023 10:03:41 +0300 Subject: [PATCH 20/43] WIP fixing angles --- .../kotlin/space/kscience/controls/misc/timeMeta.kt} | 8 ++++---- .../kscience/controls/modbus/ModbusRegistryMap.kt | 1 + .../kscience/controls/opcua/client/MetaBsdParser.kt | 10 ++++++---- 3 files changed, 11 insertions(+), 8 deletions(-) rename controls-core/src/{jvmMain/kotlin/space/kscience/controls/misc/javaTimeMeta.kt => commonMain/kotlin/space/kscience/controls/misc/timeMeta.kt} (69%) diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/misc/javaTimeMeta.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/misc/timeMeta.kt similarity index 69% rename from controls-core/src/jvmMain/kotlin/space/kscience/controls/misc/javaTimeMeta.kt rename to controls-core/src/commonMain/kotlin/space/kscience/controls/misc/timeMeta.kt index 891c30b..11683d9 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/misc/javaTimeMeta.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/misc/timeMeta.kt @@ -1,18 +1,18 @@ package space.kscience.controls.misc +import kotlinx.datetime.Instant import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.long -import java.time.Instant // TODO move to core public fun Instant.toMeta(): Meta = Meta { - "seconds" put epochSecond - "nanos" put nano + "seconds" put epochSeconds + "nanos" put nanosecondsOfSecond } -public fun Meta.instant(): Instant = value?.long?.let { Instant.ofEpochMilli(it) } ?: Instant.ofEpochSecond( +public fun Meta.instant(): Instant = value?.long?.let { Instant.fromEpochMilliseconds(it) } ?: Instant.fromEpochSeconds( get("seconds")?.long ?: 0L, get("nanos")?.long ?: 0L, ) \ No newline at end of file diff --git a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt index 80e997a..22f2619 100644 --- a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt +++ b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt @@ -142,6 +142,7 @@ public abstract class ModbusRegistryMap { } public fun print(map: ModbusRegistryMap, to: Appendable = System.out) { + validate(map) map.entries.entries.sortedBy { it.key.address }.forEach { (key, description) -> val typeString = when (key) { is ModbusRegistryKey.Coil -> "Coil" diff --git a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MetaBsdParser.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MetaBsdParser.kt index 74257af..574343e 100644 --- a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MetaBsdParser.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/MetaBsdParser.kt @@ -1,5 +1,7 @@ package space.kscience.controls.opcua.client +import kotlinx.datetime.toJavaInstant +import kotlinx.datetime.toKotlinInstant import org.eclipse.milo.opcua.binaryschema.AbstractCodec import org.eclipse.milo.opcua.binaryschema.parser.BsdParser import org.eclipse.milo.opcua.stack.core.UaSerializationException @@ -66,7 +68,7 @@ internal fun opcToMeta(value: Any?): Meta = when (value) { is Boolean -> Meta(value.asValue()) is String -> Meta(value.asValue()) is Char -> Meta(value.toString().asValue()) - is DateTime -> value.javaInstant.toMeta() + is DateTime -> value.javaInstant.toKotlinInstant().toMeta() is UUID -> Meta(value.toString().asValue()) is QualifiedName -> Meta { "namespaceIndex" put value.namespaceIndex @@ -79,9 +81,9 @@ internal fun opcToMeta(value: Any?): Meta = when (value) { is DataValue -> Meta { "value" put opcToMeta(value.value) // need SerializationContext to do that properly value.statusCode?.value?.let { "status" put Meta(it.asValue()) } - value.sourceTime?.javaInstant?.let { "sourceTime" put it.toMeta() } + value.sourceTime?.javaInstant?.let { "sourceTime" put it.toKotlinInstant().toMeta() } value.sourcePicoseconds?.let { "sourcePicoseconds" put Meta(it.asValue()) } - value.serverTime?.javaInstant?.let { "serverTime" put it.toMeta() } + value.serverTime?.javaInstant?.let { "serverTime" put it.toKotlinInstant().toMeta() } value.serverPicoseconds?.let { "serverPicoseconds" put Meta(it.asValue()) } } is ByteString -> Meta(value.bytesOrEmpty().asValue()) @@ -145,7 +147,7 @@ internal class MetaStructureCodec( "Float" -> member.value?.numberOrNull?.toFloat() "Double" -> member.value?.numberOrNull?.toDouble() "String" -> member.string - "DateTime" -> DateTime(member.instant()) + "DateTime" -> DateTime(member.instant().toJavaInstant()) "Guid" -> member.string?.let { UUID.fromString(it) } "ByteString" -> member.value?.list?.let { list -> ByteString(list.map { it.number.toByte() }.toByteArray()) From 20951a0b0f47609b30ecf90ffca650f76639bbe8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 10 Jun 2023 16:17:54 +0300 Subject: [PATCH 21/43] Modbus revision acording to modern standards --- .../space/kscience/controls/api/Device.kt | 17 +- .../kscience/controls/spec/DeviceBase.kt | 36 +- .../kscience/controls/spec/DeviceBySpec.kt | 5 +- .../controls/modbus/DeviceProcessImage.kt | 321 +++++++++++------- .../kscience/controls/modbus/ModbusDevice.kt | 105 +++--- .../controls/modbus/ModbusDeviceBySpec.kt | 15 +- .../controls/modbus/ModbusRegistryMap.kt | 147 ++++---- 7 files changed, 386 insertions(+), 260 deletions(-) diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Device.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Device.kt index 5056123..4fc6365 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Device.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/Device.kt @@ -9,10 +9,21 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import space.kscience.controls.api.Device.Companion.DEVICE_TARGET import space.kscience.dataforge.context.ContextAware +import space.kscience.dataforge.context.info +import space.kscience.dataforge.context.logger import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.misc.Type import space.kscience.dataforge.names.Name +/** + * A lifecycle state of a device + */ +public enum class DeviceLifecycleState{ + INIT, + OPEN, + CLOSED +} /** * General interface describing a managed Device. @@ -79,12 +90,16 @@ public interface Device : AutoCloseable, ContextAware, CoroutineScope { public suspend fun open(): Unit = Unit /** - * Close and terminate the device. This function does not wait for device to be closed. + * Close and terminate the device. This function does not wait for the device to be closed. */ override fun close() { + logger.info { "Device $this is closed" } cancel("The device is closed") } + @DFExperimental + public val lifecycleState: DeviceLifecycleState + public companion object { public const val DEVICE_TARGET: String = "device" } 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 034485f..56f5aa9 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 @@ -1,15 +1,16 @@ package space.kscience.controls.spec -import kotlinx.coroutines.Job -import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.* import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import space.kscience.controls.api.* import space.kscience.dataforge.context.Context -import space.kscience.dataforge.context.Global +import space.kscience.dataforge.context.error +import space.kscience.dataforge.context.logger import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.misc.DFExperimental import kotlin.coroutines.CoroutineContext @@ -37,7 +38,7 @@ private suspend fun DeviceActionSpec.executeWithMeta * A base abstractions for [Device], introducing specifications for properties */ public abstract class DeviceBase( - override val context: Context = Global, + final override val context: Context, override val meta: Meta = Meta.EMPTY, ) : Device { @@ -58,9 +59,16 @@ public abstract class DeviceBase( get() = actions.values.map { it.descriptor } override val coroutineContext: CoroutineContext by lazy { - context.coroutineContext + SupervisorJob(context.coroutineContext[Job]) + context.newCoroutineContext( + SupervisorJob(context.coroutineContext[Job]) + + CoroutineName("Device $this") + + CoroutineExceptionHandler { _, throwable -> + logger.error(throwable) { "Exception in device $this job" } + } + ) } + /** * Logical state store */ @@ -149,5 +157,23 @@ public abstract class DeviceBase( return spec.executeWithMeta(self, argument ?: Meta.EMPTY) } + @DFExperimental + override var lifecycleState: DeviceLifecycleState = DeviceLifecycleState.INIT + protected set + + @OptIn(DFExperimental::class) + override suspend fun open() { + super.open() + lifecycleState = DeviceLifecycleState.OPEN + } + + @OptIn(DFExperimental::class) + override fun close() { + lifecycleState = DeviceLifecycleState.CLOSED + super.close() + } + + abstract override fun toString(): String + } diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBySpec.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBySpec.kt index e8a071c..9309224 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBySpec.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/spec/DeviceBySpec.kt @@ -2,7 +2,6 @@ package space.kscience.controls.spec import space.kscience.controls.api.Device import space.kscience.dataforge.context.Context -import space.kscience.dataforge.context.Global import space.kscience.dataforge.meta.Meta /** @@ -11,7 +10,7 @@ import space.kscience.dataforge.meta.Meta */ public open class DeviceBySpec( public val spec: DeviceSpec, - context: Context = Global, + context: Context, meta: Meta = Meta.EMPTY, ) : DeviceBase(context, meta) { override val properties: Map> get() = spec.properties @@ -26,4 +25,6 @@ public open class DeviceBySpec( self.onClose() super.close() } + + override fun toString(): String = "Device(spec=$spec)" } \ No newline at end of file diff --git a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt index 47acb4a..3352f1a 100644 --- a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt +++ b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt @@ -4,6 +4,7 @@ import com.ghgande.j2mod.modbus.procimg.* import io.ktor.utils.io.core.buildPacket import io.ktor.utils.io.core.readByteBuffer import io.ktor.utils.io.core.writeShort +import kotlinx.coroutines.launch import space.kscience.controls.api.Device import space.kscience.controls.spec.DevicePropertySpec import space.kscience.controls.spec.WritableDevicePropertySpec @@ -11,138 +12,206 @@ import space.kscience.controls.spec.set import space.kscience.controls.spec.useProperty -public class DeviceToModbusMapping private constructor( - private val mapping: Map, ModbusRegistryKey>, -) : Map, ModbusRegistryKey> by mapping { - public class Builder { - private val mapping = HashMap, ModbusRegistryKey>() +public class DeviceProcessImageBuilder( + private val device: D, + public val image: ProcessImageImplementation, +) { - public fun bind(propertySpec: DevicePropertySpec, key: ModbusRegistryKey.DiscreteInput) { - mapping[propertySpec] = key - } - - public fun bind(propertySpec: WritableDevicePropertySpec, key: ModbusRegistryKey.Coil) { - mapping[propertySpec] = key - } - - public fun bind(propertySpec: DevicePropertySpec, key: ModbusRegistryKey.InputRegister) { - mapping[propertySpec] = key - } - - public fun bind(propertySpec: WritableDevicePropertySpec, key: ModbusRegistryKey.HoldingRegister) { - mapping[propertySpec] = key - } - - public fun bind(propertySpec: DevicePropertySpec, key: ModbusRegistryKey.InputRange) { - mapping[propertySpec] = key - } - - public fun bind(propertySpec: WritableDevicePropertySpec, key: ModbusRegistryKey.HoldingRange) { - mapping[propertySpec] = key - } - - public fun build(): DeviceToModbusMapping = DeviceToModbusMapping(mapping) + public fun bind( + key: ModbusRegistryKey.Coil, + block: D.(ObservableDigitalOut) -> Unit = {}, + ): ObservableDigitalOut { + val coil = ObservableDigitalOut() + device.block(coil) + image.addDigitalOut(key.address, coil) + return coil } + + public fun bind( + key: ModbusRegistryKey.Coil, + propertySpec: WritableDevicePropertySpec, + ): ObservableDigitalOut = bind(key) { coil -> + coil.addObserver { _, _ -> + device[propertySpec] = coil.isSet + } + device.useProperty(propertySpec) { value -> + coil.set(value) + } + } + + public fun bind( + key: ModbusRegistryKey.DiscreteInput, + block: D.(SimpleDigitalIn) -> Unit = {}, + ): DigitalIn { + val input = SimpleDigitalIn() + device.block(input) + image.addDigitalIn(key.address, input) + return input + } + + public fun bind( + key: ModbusRegistryKey.DiscreteInput, + propertySpec: DevicePropertySpec, + ): DigitalIn = bind(key) { input -> + device.useProperty(propertySpec) { value -> + input.set(value) + } + } + + public fun bind( + key: ModbusRegistryKey.InputRegister, + block: D.(SimpleInputRegister) -> Unit = {}, + ): SimpleInputRegister { + val input = SimpleInputRegister() + device.block(input) + image.addInputRegister(key.address, input) + return input + } + + public fun bind( + key: ModbusRegistryKey.InputRegister, + propertySpec: DevicePropertySpec, + ): SimpleInputRegister = bind(key) { input -> + device.useProperty(propertySpec) { value -> + input.setValue(value) + } + } + + public fun bind( + key: ModbusRegistryKey.HoldingRegister, + block: D.(ObservableRegister) -> Unit = {}, + ): ObservableRegister { + val register = ObservableRegister() + device.block(register) + image.addRegister(key.address, register) + return register + } + + public fun bind( + key: ModbusRegistryKey.HoldingRegister, + propertySpec: WritableDevicePropertySpec, + ): ObservableRegister = bind(key) { register -> + register.addObserver { _, _ -> + device[propertySpec] = register.toShort() + } + device.useProperty(propertySpec) { value -> + register.setValue(value) + } + } + + public fun bind(key: ModbusRegistryKey.InputRange, propertySpec: DevicePropertySpec) { + val registers = List(key.count) { + SimpleInputRegister() + } + + registers.forEachIndexed { index, register -> + image.addInputRegister(key.address + index, register) + } + + device.useProperty(propertySpec) { value -> + val packet = buildPacket { + key.format.writeObject(this, value) + }.readByteBuffer() + registers.forEachIndexed { index, register -> + register.setValue(packet.getShort(index * 2)) + } + } + } + + public fun bind(key: ModbusRegistryKey.HoldingRange, propertySpec: WritableDevicePropertySpec) { + val registers = List(key.count) { + ObservableRegister() + } + registers.forEachIndexed { index, register -> + register.addObserver { _, _ -> + val packet = buildPacket { + registers.forEach { value -> + writeShort(value.toShort()) + } + } + device[propertySpec] = key.format.readObject(packet) + } + image.addRegister(key.address + index, register) + } + + device.useProperty(propertySpec) { value -> + val packet = buildPacket { + key.format.writeObject(this, value) + }.readByteBuffer() + registers.forEachIndexed { index, observableRegister -> + observableRegister.setValue(packet.getShort(index * 2)) + } + } + } + + public fun bindAction( + key: ModbusRegistryKey.Coil, + action: suspend D.(Boolean) -> Unit, + ): ObservableDigitalOut { + val coil = ObservableDigitalOut() + coil.addObserver { _, _ -> + device.launch { + device.action(coil.isSet) + } + } + image.addDigitalOut(key.address, coil) + return coil + } + + public fun bindAction( + key: ModbusRegistryKey.HoldingRegister, + action: suspend D.(Short) -> Unit, + ): ObservableRegister { + val register = ObservableRegister() + register.addObserver { _, _ -> + + with(device) { + launch { + action(register.toShort()) + } + } + } + image.addRegister(key.address, register) + return register + } + + public fun bindAction( + key: ModbusRegistryKey.HoldingRange, + action: suspend D.(T) -> Unit, + ): List { + val registers = List(key.count) { + ObservableRegister() + } + registers.forEachIndexed { index, register -> + register.addObserver { _, _ -> + val packet = buildPacket { + registers.forEach { value -> + writeShort(value.toShort()) + } + } + device.launch { + device.action(key.format.readObject(packet)) + } + } + image.addRegister(key.address + index, register) + } + + return registers + } + } -public inline fun DeviceToModbusMapping(block: DeviceToModbusMapping.Builder.()->Unit): DeviceToModbusMapping = - DeviceToModbusMapping.Builder().apply(block).build() -@Suppress("UNCHECKED_CAST") -public fun D.toProcessImage(mapping: DeviceToModbusMapping): ProcessImage { +public fun D.bindProcessImage( + openOnBind: Boolean = true, + binding: DeviceProcessImageBuilder.() -> Unit, +): ProcessImage { val image = SimpleProcessImage() - mapping.forEach { (spec, key) -> - when (key) { - is ModbusRegistryKey.Coil -> { - spec as WritableDevicePropertySpec - val coil = ObservableDigitalOut() - coil.addObserver { _, _ -> - set(spec, coil.isSet) - } - image.setDigitalOut(key.address, coil) - useProperty(spec) { value -> - coil.set(value) - } - } - - is ModbusRegistryKey.DiscreteInput -> { - spec as DevicePropertySpec - val input = SimpleDigitalIn() - image.setDigitalIn(key.address, input) - useProperty(spec) { value -> - input.set(value) - } - } - - is ModbusRegistryKey.HoldingRegister -> { - spec as WritableDevicePropertySpec - val register = ObservableRegister() - register.addObserver { _, _ -> - set(spec, register.toShort()) - } - image.setRegister(key.address, register) - useProperty(spec) { value -> - register.setValue(value) - } - } - - is ModbusRegistryKey.InputRegister -> { - spec as DevicePropertySpec - val input = SimpleInputRegister() - image.setRegister(key.address, input) - useProperty(spec) { value -> - input.setValue(value) - } - } - - is ModbusRegistryKey.HoldingRange<*> -> { - spec as WritableDevicePropertySpec - key as ModbusRegistryKey.HoldingRange - val registers = List(key.count) { - ObservableRegister() - } - registers.forEachIndexed { index, register -> - register.addObserver { _, _ -> - val packet = buildPacket { - registers.forEach { value -> - writeShort(value.toShort()) - } - } - set(spec, key.format.readObject(packet)) - } - image.setRegister(key.address + index, register) - } - - useProperty(spec) { value -> - val packet = buildPacket { - key.format.writeObject(this, value) - }.readByteBuffer() - registers.forEachIndexed { index, observableRegister -> - observableRegister.setValue(packet.getShort(index * 2)) - } - } - } - - is ModbusRegistryKey.InputRange<*> -> { - spec as DevicePropertySpec - key as ModbusRegistryKey.InputRange - val registers = List(key.count) { - SimpleInputRegister() - } - - useProperty(spec) { value -> - val packet = buildPacket { - key.format.writeObject(this, value) - }.readByteBuffer() - registers.forEachIndexed { index, register -> - register.setValue(packet.getShort(index * 2)) - } - } - } + DeviceProcessImageBuilder(this, image).apply(binding) + if (openOnBind) { + launch { + open() } } return image -} - -public inline fun D.toProcessImage(block: DeviceToModbusMapping.Builder.()->Unit): ProcessImage = - toProcessImage(DeviceToModbusMapping(block)) \ No newline at end of file +} \ No newline at end of file diff --git a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDevice.kt b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDevice.kt index 1c442aa..ea4330c 100644 --- a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDevice.kt +++ b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDevice.kt @@ -3,7 +3,7 @@ package space.kscience.controls.modbus import com.ghgande.j2mod.modbus.facade.AbstractModbusMaster import com.ghgande.j2mod.modbus.procimg.InputRegister import com.ghgande.j2mod.modbus.procimg.Register -import com.ghgande.j2mod.modbus.procimg.SimpleRegister +import com.ghgande.j2mod.modbus.procimg.SimpleInputRegister import com.ghgande.j2mod.modbus.util.BitVector import io.ktor.utils.io.core.ByteReadPacket import io.ktor.utils.io.core.buildPacket @@ -73,7 +73,7 @@ public interface ModbusDevice : Device { val buffer = buildPacket { format.writeObject(this, value) }.readByteBuffer() - writeHoldingRegisters(address,buffer) + writeHoldingRegisters(address, buffer) } } @@ -81,36 +81,36 @@ public interface ModbusDevice : Device { /** * Read multiple sequential modbus coils (bit-values) */ -public fun ModbusDevice.readCoils(ref: Int, count: Int): BitVector = - master.readCoils(clientId, ref, count) +public fun ModbusDevice.readCoils(address: Int, count: Int): BitVector = + master.readCoils(clientId, address, count) -public fun ModbusDevice.readCoil(ref: Int): Boolean = - master.readCoils(clientId, ref, 1).getBit(0) +public fun ModbusDevice.readCoil(address: Int): Boolean = + master.readCoils(clientId, address, 1).getBit(0) -public fun ModbusDevice.writeCoils(ref: Int, values: BooleanArray) { +public fun ModbusDevice.writeCoils(address: Int, values: BooleanArray) { val bitVector = BitVector(values.size) values.forEachIndexed { index, value -> bitVector.setBit(index, value) } - master.writeMultipleCoils(clientId, ref, bitVector) + master.writeMultipleCoils(clientId, address, bitVector) } -public fun ModbusDevice.writeCoil(ref: Int, value: Boolean) { - master.writeCoil(clientId, ref, value) +public fun ModbusDevice.writeCoil(address: Int, value: Boolean) { + master.writeCoil(clientId, address, value) } public fun ModbusDevice.writeCoil(key: ModbusRegistryKey.Coil, value: Boolean) { master.writeCoil(clientId, key.address, value) } -public fun ModbusDevice.readInputDiscretes(ref: Int, count: Int): BitVector = - master.readInputDiscretes(clientId, ref, count) +public fun ModbusDevice.readInputDiscretes(address: Int, count: Int): BitVector = + master.readInputDiscretes(clientId, address, count) -public fun ModbusDevice.readInputDiscrete(ref: Int): Boolean = - master.readInputDiscretes(clientId, ref, 1).getBit(0) +public fun ModbusDevice.readInputDiscrete(address: Int): Boolean = + master.readInputDiscretes(clientId, address, 1).getBit(0) -public fun ModbusDevice.readInputRegisters(ref: Int, count: Int): List = - master.readInputRegisters(clientId, ref, count).toList() +public fun ModbusDevice.readInputRegisters(address: Int, count: Int): List = + master.readInputRegisters(clientId, address, count).toList() private fun Array.toBuffer(): ByteBuffer { val buffer: ByteBuffer = ByteBuffer.allocate(size * 2) @@ -128,79 +128,82 @@ private fun Array.toPacket(): ByteReadPacket = buildPacket { } } -public fun ModbusDevice.readInputRegistersToBuffer(ref: Int, count: Int): ByteBuffer = - master.readInputRegisters(clientId, ref, count).toBuffer() +public fun ModbusDevice.readInputRegistersToBuffer(address: Int, count: Int): ByteBuffer = + master.readInputRegisters(clientId, address, count).toBuffer() -public fun ModbusDevice.readInputRegistersToPacket(ref: Int, count: Int): ByteReadPacket = - master.readInputRegisters(clientId, ref, count).toPacket() +public fun ModbusDevice.readInputRegistersToPacket(address: Int, count: Int): ByteReadPacket = + master.readInputRegisters(clientId, address, count).toPacket() -public fun ModbusDevice.readDoubleInput(ref: Int): Double = - readInputRegistersToBuffer(ref, Double.SIZE_BYTES).getDouble() +public fun ModbusDevice.readDoubleInput(address: Int): Double = + readInputRegistersToBuffer(address, Double.SIZE_BYTES).getDouble() -public fun ModbusDevice.readInputRegister(ref: Int): Short = - readInputRegisters(ref, 1).first().toShort() +public fun ModbusDevice.readInputRegister(address: Int): Short = + readInputRegisters(address, 1).first().toShort() -public fun ModbusDevice.readHoldingRegisters(ref: Int, count: Int): List = - master.readMultipleRegisters(clientId, ref, count).toList() +public fun ModbusDevice.readHoldingRegisters(address: Int, count: Int): List = + master.readMultipleRegisters(clientId, address, count).toList() /** * Read a number of registers to a [ByteBuffer] - * @param ref address of a register + * @param address of a register * @param count number of 2-bytes registers to read. Buffer size is 2*[count] */ -public fun ModbusDevice.readHoldingRegistersToBuffer(ref: Int, count: Int): ByteBuffer = - master.readMultipleRegisters(clientId, ref, count).toBuffer() +public fun ModbusDevice.readHoldingRegistersToBuffer(address: Int, count: Int): ByteBuffer = + master.readMultipleRegisters(clientId, address, count).toBuffer() -public fun ModbusDevice.readHoldingRegistersToPacket(ref: Int, count: Int): ByteReadPacket = - master.readMultipleRegisters(clientId, ref, count).toPacket() +public fun ModbusDevice.readHoldingRegistersToPacket(address: Int, count: Int): ByteReadPacket = + master.readMultipleRegisters(clientId, address, count).toPacket() -public fun ModbusDevice.readDoubleRegister(ref: Int): Double = - readHoldingRegistersToBuffer(ref, Double.SIZE_BYTES).getDouble() +public fun ModbusDevice.readDoubleRegister(address: Int): Double = + readHoldingRegistersToBuffer(address, Double.SIZE_BYTES).getDouble() -public fun ModbusDevice.readHoldingRegister(ref: Int): Short = - readHoldingRegisters(ref, 1).first().toShort() +public fun ModbusDevice.readHoldingRegister(address: Int): Short = + readHoldingRegisters(address, 1).first().toShort() -public fun ModbusDevice.writeHoldingRegisters(ref: Int, values: ShortArray): Int = +public fun ModbusDevice.writeHoldingRegisters(address: Int, values: ShortArray): Int = master.writeMultipleRegisters( clientId, - ref, - Array(values.size) { SimpleRegister().apply { setValue(values[it]) } } + address, + Array(values.size) { SimpleInputRegister(values[it].toInt()) } ) -public fun ModbusDevice.writeHoldingRegister(ref: Int, value: Short): Int = +public fun ModbusDevice.writeHoldingRegister(address: Int, value: Short): Int = master.writeSingleRegister( clientId, - ref, - SimpleRegister().apply { setValue(value) } + address, + SimpleInputRegister(value.toInt()) ) -public fun ModbusDevice.writeHoldingRegisters(ref: Int, buffer: ByteBuffer): Int { +public fun ModbusDevice.writeHoldingRegister(key: ModbusRegistryKey.HoldingRegister, value: Short): Int = + writeHoldingRegister(key.address, value) + +public fun ModbusDevice.writeHoldingRegisters(address: Int, buffer: ByteBuffer): Int { val array: ShortArray = ShortArray(buffer.limit().floorDiv(2)) { buffer.getShort(it * 2) } - return writeHoldingRegisters(ref, array) + return writeHoldingRegisters(address, array) } -public fun ModbusDevice.writeShortRegister(ref: Int, value: Short) { - master.writeSingleRegister(ref, SimpleRegister().apply { setValue(value) }) +public fun ModbusDevice.writeShortRegister(address: Int, value: Short) { + master.writeSingleRegister(address, SimpleInputRegister(value.toInt())) } public fun ModbusDevice.modbusRegister( - ref: Int, + address: Int, ): ReadWriteProperty = object : ReadWriteProperty { - override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Short = readHoldingRegister(ref) + override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Short = readHoldingRegister(address) override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Short) { - writeHoldingRegister(ref, value) + writeHoldingRegister(address, value) } } public fun ModbusDevice.modbusDoubleRegister( - ref: Int, + address: Int, ): ReadWriteProperty = object : ReadWriteProperty { - override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Double = readDoubleRegister(ref) + override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Double = readDoubleRegister(address) override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Double) { val buffer = ByteBuffer.allocate(Double.SIZE_BYTES).apply { putDouble(value) } - writeHoldingRegisters(ref, buffer) + writeHoldingRegisters(address, buffer) } } 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 e1acd4b..1956e40 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 @@ -17,8 +17,21 @@ public open class ModbusDeviceBySpec( spec: DeviceSpec, override val clientId: Int, override val master: AbstractModbusMaster, + private val disposeMasterOnClose: Boolean = true, meta: Meta = Meta.EMPTY, -) : ModbusDevice, DeviceBySpec(spec, context, meta) +) : ModbusDevice, DeviceBySpec(spec, context, meta){ + override suspend fun open() { + master.connect() + super.open() + } + + override fun close() { + if(disposeMasterOnClose){ + master.disconnect() + } + super.close() + } +} public class ModbusHub( diff --git a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt index 22f2619..a6fc894 100644 --- a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt +++ b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt @@ -7,63 +7,48 @@ public sealed class ModbusRegistryKey { public abstract val address: Int public open val count: Int = 1 + /** * Read-only boolean value */ - public data class Coil(override val address: Int) : ModbusRegistryKey() { - init { - require(address in 1..9999) { "Coil address must be in 1..9999 range" } - } - } + public data class Coil(override val address: Int) : ModbusRegistryKey() /** * Read-write boolean value */ - public data class DiscreteInput(override val address: Int) : ModbusRegistryKey() { - init { - require(address in 10001..19999) { "DiscreteInput address must be in 10001..19999 range" } - } - } + public data class DiscreteInput(override val address: Int) : ModbusRegistryKey() /** * Read-only binary value */ - public data class InputRegister(override val address: Int) : ModbusRegistryKey() { - init { - require(address in 20001..29999) { "InputRegister address must be in 20001..29999 range" } - } + public open class InputRegister(override val address: Int) : ModbusRegistryKey() { + override fun toString(): String = "InputRegister(address=$address)" } - public data class InputRange( - override val address: Int, + public class InputRange( + address: Int, override val count: Int, public val format: IOFormat, - ) : ModbusRegistryKey() { + ) : InputRegister(address) { public val endAddress: Int get() = address + count + override fun toString(): String = "InputRange(count=$count, format=$format)" + - init { - require(address in 20001..29999) { "InputRange begin address is $address, but must be in 20001..29999 range" } - require(endAddress in 20001..29999) { "InputRange end address is ${endAddress}, but must be in 20001..29999 range" } - } } - public data class HoldingRegister(override val address: Int) : ModbusRegistryKey() { - init { - require(address in 30001..39999) { "HoldingRegister address must be in 30001..39999 range" } - } + public open class HoldingRegister(override val address: Int) : ModbusRegistryKey() { + override fun toString(): String = "HoldingRegister(address=$address)" } - public data class HoldingRange( - override val address: Int, + public class HoldingRange( + address: Int, override val count: Int, public val format: IOFormat, - ) : ModbusRegistryKey() { + ) : HoldingRegister(address) { public val endAddress: Int get() = address + count + override fun toString(): String = "HoldingRange(count=$count, format=$format)" + - init { - require(address in 30001..39999) { "HoldingRange begin address is $address, but must be in 30001..39999 range" } - require(endAddress in 30001..39999) { "HoldingRange end address is ${endAddress}, but must be in 30001..39999 range" } - } } } @@ -81,15 +66,10 @@ public abstract class ModbusRegistryMap { protected fun coil(address: Int, description: String = ""): ModbusRegistryKey.Coil = register(ModbusRegistryKey.Coil(address), description) - protected fun coilByOffset(offset: Int, description: String = ""): ModbusRegistryKey.Coil = - register(ModbusRegistryKey.Coil(offset), description) protected fun discrete(address: Int, description: String = ""): ModbusRegistryKey.DiscreteInput = register(ModbusRegistryKey.DiscreteInput(address), description) - protected fun discreteByOffset(offset: Int, description: String = ""): ModbusRegistryKey.DiscreteInput = - register(ModbusRegistryKey.DiscreteInput(10000 + offset), description) - protected fun input(address: Int, description: String = ""): ModbusRegistryKey.InputRegister = register(ModbusRegistryKey.InputRegister(address), description) @@ -101,17 +81,6 @@ public abstract class ModbusRegistryMap { ): ModbusRegistryKey.InputRange = register(ModbusRegistryKey.InputRange(address, count, reader), description) - protected fun inputByOffset(offset: Int, description: String = ""): ModbusRegistryKey.InputRegister = - register(ModbusRegistryKey.InputRegister(20000 + offset), description) - - protected fun inputByOffset( - offset: Int, - count: Int, - reader: IOFormat, - description: String = "", - ): ModbusRegistryKey.InputRange = - register(ModbusRegistryKey.InputRange(20000 + offset, count, reader), description) - protected fun register(address: Int, description: String = ""): ModbusRegistryKey.HoldingRegister = register(ModbusRegistryKey.HoldingRegister(address), description) @@ -123,40 +92,70 @@ public abstract class ModbusRegistryMap { ): ModbusRegistryKey.HoldingRange = register(ModbusRegistryKey.HoldingRange(address, count, format), description) - protected fun registerByOffset(offset: Int, description: String = ""): ModbusRegistryKey.HoldingRegister = - register(ModbusRegistryKey.HoldingRegister(30000 + offset), description) - - protected fun registerByOffset( - offset: Int, - count: Int, - format: IOFormat, - description: String = "", - ): ModbusRegistryKey.HoldingRange = - register(ModbusRegistryKey.HoldingRange(offset + 30000, count, format), description) - public companion object { public fun validate(map: ModbusRegistryMap) { - map.entries.keys.sortedBy { it.address }.zipWithNext().forEach { (l, r) -> - if (l.address + l.count > r.address) error("Key $l overlaps with key $r") + var lastCoil: ModbusRegistryKey.Coil? = null + var lastDiscreteInput: ModbusRegistryKey.DiscreteInput? = null + var lastInput: ModbusRegistryKey.InputRegister? = null + var lastRegister: ModbusRegistryKey.HoldingRegister? = null + map.entries.keys.sortedBy { it.address }.forEach { key -> + when (key) { + is ModbusRegistryKey.Coil -> if (lastCoil?.let { key.address >= it.address + it.count } != false) { + lastCoil = key + } else { + error("Key $lastCoil overlaps with key $key") + } + + is ModbusRegistryKey.DiscreteInput -> if (lastDiscreteInput?.let { key.address >= it.address + it.count } != false) { + lastDiscreteInput = key + } else { + error("Key $lastDiscreteInput overlaps with key $key") + } + + is ModbusRegistryKey.InputRegister -> if (lastInput?.let { key.address >= it.address + it.count } != false) { + lastInput = key + } else { + error("Key $lastInput overlaps with key $key") + } + + is ModbusRegistryKey.HoldingRegister -> if (lastRegister?.let { key.address >= it.address + it.count } != false) { + lastRegister = key + } else { + error("Key $lastRegister overlaps with key $key") + } + } } } + private val ModbusRegistryKey.sectionNumber + get() = when (this) { + is ModbusRegistryKey.Coil -> 1 + is ModbusRegistryKey.DiscreteInput -> 2 + is ModbusRegistryKey.HoldingRegister -> 4 + is ModbusRegistryKey.InputRegister -> 3 + } + public fun print(map: ModbusRegistryMap, to: Appendable = System.out) { validate(map) - map.entries.entries.sortedBy { it.key.address }.forEach { (key, description) -> - val typeString = when (key) { - is ModbusRegistryKey.Coil -> "Coil" - is ModbusRegistryKey.DiscreteInput -> "Discrete" - is ModbusRegistryKey.HoldingRange<*>, is ModbusRegistryKey.HoldingRegister -> "Register" - is ModbusRegistryKey.InputRange<*>, is ModbusRegistryKey.InputRegister -> "Input" + map.entries.entries + .sortedWith( + Comparator.comparingInt?> { it.key.sectionNumber } + .thenComparingInt { it.key.address } + ) + .forEach { (key, description) -> + val typeString = when (key) { + is ModbusRegistryKey.Coil -> "Coil" + is ModbusRegistryKey.DiscreteInput -> "Discrete" + is ModbusRegistryKey.HoldingRegister -> "Register" + is ModbusRegistryKey.InputRegister -> "Input" + } + val rangeString = if (key.count == 1) { + key.address.toString() + } else { + "${key.address} - ${key.address + key.count}" + } + to.appendLine("${typeString}\t$rangeString\t$description") } - val rangeString = if (key.count == 1) { - key.address.toString() - } else { - "${key.address} - ${key.address + key.count}" - } - to.appendLine("${typeString}\t$rangeString\t$description") - } } } } From d0d8869a1e477c854a066d8bdc634eab72c30f4e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 10 Jun 2023 16:28:11 +0300 Subject: [PATCH 22/43] Eternal happiness --- .../kotlin/space/kscience/controls/modbus/ModbusDeviceBySpec.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1956e40..916187f 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 @@ -22,7 +22,7 @@ public open class ModbusDeviceBySpec( ) : ModbusDevice, DeviceBySpec(spec, context, meta){ override suspend fun open() { master.connect() - super.open() + super.open() } override fun close() { From d1e9b0a5a5a4fa9475496b424368e34eaae63b73 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 10 Jun 2023 16:54:06 +0300 Subject: [PATCH 23/43] Add device lifecycle state --- .../kotlin/space/kscience/controls/client/DeviceClient.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt index 53a7704..699a766 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.sync.withLock import space.kscience.controls.api.* import space.kscience.dataforge.context.Context import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.broadcast @@ -95,6 +96,9 @@ public class DeviceClient( it.action == actionName && it.requestId == id }.result } + + @DFExperimental + override val lifecycleState: DeviceLifecycleState = DeviceLifecycleState.OPEN } /** From 4d4a9fba1cc33947a548670dabf0973d663d904d Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 29 Jun 2023 09:19:05 +0300 Subject: [PATCH 24/43] MQTT topic selectors --- .../space/kscience/controls/demo/DemoControllerView.kt | 3 ++- .../kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt | 7 ++++--- .../space/kscience/magix/rabbit/RabbitMQMagixEndpoint.kt | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) 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 9eb8244..edb199e 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 @@ -36,8 +36,9 @@ class DemoController : Controller(), ContextAware { var visualizer: ApplicationEngine? = null var opcUaServer: OpcUaServer = OpcUaServer { setApplicationName(LocalizedText.english("space.kscience.controls.opcua")) + endpoint { - setBindPort(9999) + setBindPort(4840) //use default endpoint } } diff --git a/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt b/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt index c9ac7f7..66cf299 100644 --- a/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt +++ b/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt @@ -17,7 +17,8 @@ import java.util.* public class MqttMagixEndpoint( serverHost: String, clientId: String = UUID.randomUUID().toString(), - public val topic: String = DEFAULT_MAGIX_TOPIC_NAME, + private val broadcastTopicBuilder: (MagixMessage) -> String = { DEFAULT_MAGIX_TOPIC_NAME }, + private val subscribeTopicBuilder: (MagixMessageFilter) -> String = { DEFAULT_MAGIX_TOPIC_NAME }, public val qos: MqttQos = MqttQos.AT_LEAST_ONCE, ) : MagixEndpoint, AutoCloseable { @@ -36,7 +37,7 @@ public class MqttMagixEndpoint( override fun subscribe(filter: MagixMessageFilter): Flow = callbackFlow { connection.await() client.subscribeWith() - .topicFilter(topic) + .topicFilter(subscribeTopicBuilder(filter)) .qos(qos) .callback { published -> val message = MagixEndpoint.magixJson.decodeFromString( @@ -54,7 +55,7 @@ public class MqttMagixEndpoint( override suspend fun broadcast(message: MagixMessage) { connection.await() - client.publishWith().topic(topic).qos(qos).payload( + client.publishWith().topic(broadcastTopicBuilder(message)).qos(qos).payload( MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message).encodeToByteArray() ).send() } diff --git a/magix/magix-rabbit/src/main/kotlin/space/kscience/magix/rabbit/RabbitMQMagixEndpoint.kt b/magix/magix-rabbit/src/main/kotlin/space/kscience/magix/rabbit/RabbitMQMagixEndpoint.kt index 33af457..561acdd 100644 --- a/magix/magix-rabbit/src/main/kotlin/space/kscience/magix/rabbit/RabbitMQMagixEndpoint.kt +++ b/magix/magix-rabbit/src/main/kotlin/space/kscience/magix/rabbit/RabbitMQMagixEndpoint.kt @@ -27,7 +27,7 @@ public class RabbitMQMagixEndpoint( } override fun subscribe(filter: MagixMessageFilter): Flow = callbackFlow { - val deliverCallback: DeliverCallback = DeliverCallback { _: String, message: Delivery -> + val deliverCallback = DeliverCallback { _: String, message: Delivery -> val magixMessage = MagixEndpoint.magixJson.decodeFromString( MagixMessage.serializer(), message.body.decodeToString() ) From c28944e10f5861c8f69390a8589e12a5b21556d4 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 23 Jul 2023 14:01:47 +0300 Subject: [PATCH 25/43] Refactor connection infrastructure --- .../opcua/client/OpcUaDeviceBySpec.kt | 18 +++--- .../controls/opcua/client/miloClient.kt | 14 ++--- .../controls/opcua/server/DeviceNameSpace.kt | 30 ++-------- .../space/kscience/controls/demo/echo/main.kt | 4 +- .../kscience/controls/demo/MassDevice.kt | 27 ++++----- .../pimotionmaster/fxDeviceProperties.kt | 14 ++--- .../kscience/magix/api/MagixFlowPlugin.kt | 22 +++++++- .../kscience/magix/connections/magixPortal.kt | 30 ++++++++++ .../magix/rsocket/RSocketMagixEndpoint.kt | 8 ++- .../rsocket/RSocketStreamMagixEndpoint.kt | 33 +++++------ .../space/kscience/magix/rsocket/withTcp.kt | 4 +- .../magix/server/RSocketMagixFlowPlugin.kt | 56 ++++++++++++++----- .../kscience/magix/server/magixModule.kt | 7 ++- .../kscince/magix/zmq/ZmqMagixEndpoint.kt | 1 - .../kscince/magix/zmq/ZmqMagixFlowPlugin.kt | 13 +++-- 15 files changed, 172 insertions(+), 109 deletions(-) create mode 100644 magix/magix-api/src/commonMain/kotlin/space/kscience/magix/connections/magixPortal.kt diff --git a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/OpcUaDeviceBySpec.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/OpcUaDeviceBySpec.kt index 821b693..eb9b688 100644 --- a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/OpcUaDeviceBySpec.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client/OpcUaDeviceBySpec.kt @@ -1,7 +1,7 @@ package space.kscience.controls.opcua.client 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.config.OpcUaClientConfigBuilder 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 @@ -12,12 +12,12 @@ import space.kscience.dataforge.context.Global import space.kscience.dataforge.meta.* -public sealed class MiloIdentity: Scheme() +public sealed class MiloIdentity : Scheme() public class MiloUsername : MiloIdentity() { - public var username: String by string{ error("Username not defined") } - public var password: String by string{ error("Password not defined") } + public var username: String by string { error("Username not defined") } + public var password: String by string { error("Password not defined") } public companion object : SchemeSpec(::MiloUsername) } @@ -35,6 +35,12 @@ public class MiloConfiguration : Scheme() { public var securityPolicy: SecurityPolicy by enum(SecurityPolicy.None) + internal fun configureClient(builder: OpcUaClientConfigBuilder) { + username?.let { + builder.setIdentityProvider(UsernameProvider(it.username, it.password)) + } + } + public companion object : SchemeSpec(::MiloConfiguration) } @@ -51,9 +57,7 @@ public open class OpcUaDeviceBySpec( context.createOpcUaClient( config.endpointUrl, securityPolicy = config.securityPolicy, - identityProvider = config.username?.let { - UsernameProvider(it.username,it.password) - } ?: AnonymousProvider() + opcClientConfig = { config.configureClient(this) } ).apply { connect().get() } 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 face6cc..8149bd9 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 @@ -3,7 +3,6 @@ package space.kscience.controls.opcua.client import org.eclipse.milo.opcua.sdk.client.OpcUaClient import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfigBuilder import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider -import org.eclipse.milo.opcua.sdk.client.api.identity.IdentityProvider import org.eclipse.milo.opcua.stack.client.security.DefaultClientCertificateValidator import org.eclipse.milo.opcua.stack.core.security.DefaultTrustListManager import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy @@ -18,14 +17,14 @@ import java.nio.file.Path import java.nio.file.Paths import java.util.* -internal fun T?.toOptional(): Optional = if(this == null) Optional.empty() else Optional.of(this) +internal fun T?.toOptional(): Optional = Optional.ofNullable(this) internal fun Context.createOpcUaClient( endpointUrl: String, //"opc.tcp://localhost:12686/milo" securityPolicy: SecurityPolicy = SecurityPolicy.Basic256Sha256, - identityProvider: IdentityProvider = AnonymousProvider(), - endpointFilter: (EndpointDescription?) -> Boolean = { securityPolicy.uri == it?.securityPolicyUri } + endpointFilter: (EndpointDescription?) -> Boolean = { securityPolicy.uri == it?.securityPolicyUri }, + opcClientConfig: OpcUaClientConfigBuilder.() -> Unit, ): OpcUaClient { val securityTempDir: Path = Paths.get(System.getProperty("java.io.tmpdir"), "client", "security") @@ -47,14 +46,15 @@ internal fun Context.createOpcUaClient( } ) { configBuilder: OpcUaClientConfigBuilder -> configBuilder - .setApplicationName(LocalizedText.english("Controls.kt")) - .setApplicationUri("urn:ru.mipt:npm:controls:opcua") + .setApplicationName(LocalizedText.english("Controls-kt")) + .setApplicationUri("urn:space.kscience:controls:opcua") // .setKeyPair(loader.getClientKeyPair()) // .setCertificate(loader.getClientCertificate()) // .setCertificateChain(loader.getClientCertificateChain()) .setCertificateValidator(certificateValidator) - .setIdentityProvider(identityProvider) + .setIdentityProvider(AnonymousProvider()) .setRequestTimeout(uint(5000)) + .apply(opcClientConfig) .build() } // .apply { diff --git a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt index 6b8e44c..010c2c0 100644 --- a/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt +++ b/controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server/DeviceNameSpace.kt @@ -12,6 +12,7 @@ import org.eclipse.milo.opcua.sdk.server.api.ManagedNamespaceWithLifecycle import org.eclipse.milo.opcua.sdk.server.api.MonitoredItem import org.eclipse.milo.opcua.sdk.server.nodes.UaFolderNode import org.eclipse.milo.opcua.sdk.server.nodes.UaNode +import org.eclipse.milo.opcua.sdk.server.nodes.UaNodeContext import org.eclipse.milo.opcua.sdk.server.nodes.UaVariableNode import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel import org.eclipse.milo.opcua.stack.core.AttributeId @@ -27,7 +28,6 @@ import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.MetaSerializer import space.kscience.dataforge.meta.ValueType import space.kscience.dataforge.names.Name -import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.plus @@ -50,25 +50,7 @@ public class DeviceNameSpace( lifecycleManager.addLifecycle(subscription) lifecycleManager.addStartupTask { - deviceManager.devices.forEach { (deviceName, device) -> - val tokenAsString = deviceName.toString() - val deviceFolder = UaFolderNode( - this.nodeContext, - newNodeId(tokenAsString), - newQualifiedName(tokenAsString), - LocalizedText.english(tokenAsString) - ) - deviceFolder.addReference( - Reference( - deviceFolder.nodeId, - Identifiers.Organizes, - Identifiers.ObjectsFolder.expanded(), - false - ) - ) - deviceFolder.registerDeviceNodes(deviceName.asName(), device) - this.nodeManager.addNode(deviceFolder) - } + nodeContext.registerHub(deviceManager, Name.EMPTY) } lifecycleManager.addLifecycle(object : Lifecycle { @@ -88,7 +70,7 @@ public class DeviceNameSpace( val node: UaVariableNode = UaVariableNode.UaVariableNodeBuilder(nodeContext).apply { - //for now use DF path as id + //for now, use DF paths as ids nodeId = newNodeId("${deviceName.tokens.joinToString("/")}/$propertyName") when { descriptor.readable && descriptor.writable -> { @@ -161,15 +143,15 @@ public class DeviceNameSpace( } //recursively add sub-devices if (device is DeviceHub) { - registerHub(device, deviceName) + nodeContext.registerHub(device, deviceName) } } - private fun UaNode.registerHub(hub: DeviceHub, namePrefix: Name) { + private fun UaNodeContext.registerHub(hub: DeviceHub, namePrefix: Name) { hub.devices.forEach { (deviceName, device) -> val tokenAsString = deviceName.toString() val deviceFolder = UaFolderNode( - this.nodeContext, + this, newNodeId(tokenAsString), newQualifiedName(tokenAsString), LocalizedText.english(tokenAsString) diff --git a/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt b/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt index 3dbb43d..f172b9a 100644 --- a/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt +++ b/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt @@ -60,14 +60,14 @@ private suspend fun MagixEndpoint.collectEcho(scope: CoroutineScope, n: Int) { @OptIn(ExperimentalTime::class) suspend fun main(): Unit = coroutineScope { launch(Dispatchers.Default) { - val server = startMagixServer(MagixFlowPlugin { _, flow -> + val server = startMagixServer(MagixFlowPlugin { _, flow, send -> val logger = LoggerFactory.getLogger("echo") //echo each message flow.onEach { message -> if (message.parentId == null) { val m = message.copy(origin = "loop", parentId = message.id, id = message.id + ".response") logger.info(m.toString()) - flow.emit(m) + send(m) } }.launchIn(this) }) diff --git a/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt b/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt index 6370d8d..6dae782 100644 --- a/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt +++ b/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt @@ -7,7 +7,6 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.datetime.Clock -import kotlinx.datetime.Instant import space.kscience.controls.client.connectToMagix import space.kscience.controls.client.controlsMagixFormat import space.kscience.controls.manager.DeviceManager @@ -21,7 +20,7 @@ import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.int import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.subscribe -import space.kscience.magix.rsocket.rSocketStreamWithTcp +import space.kscience.magix.rsocket.rSocketWithWebSockets import space.kscience.magix.server.RSocketMagixFlowPlugin import space.kscience.magix.server.startMagixServer import space.kscience.plotly.Plotly @@ -31,8 +30,10 @@ import space.kscience.plotly.plot import space.kscience.plotly.server.PlotlyUpdateMode import space.kscience.plotly.server.serve import space.kscience.plotly.server.show +import space.kscince.magix.zmq.ZmqMagixFlowPlugin import java.util.concurrent.ConcurrentHashMap import kotlin.random.Random +import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds @@ -48,7 +49,7 @@ class MassDevice(context: Context, meta: Meta) : DeviceBySpec(MassDe val value by doubleProperty { randomValue } override suspend fun MassDevice.onOpen() { - doRecurring(100.milliseconds) { + doRecurring(50.milliseconds) { read(value) } } @@ -60,13 +61,13 @@ fun main() { context.startMagixServer( RSocketMagixFlowPlugin(), -// ZmqMagixFlowPlugin() + ZmqMagixFlowPlugin() ) val numDevices = 100 - context.launch(Dispatchers.IO) { - repeat(numDevices) { + repeat(numDevices) { + context.launch(Dispatchers.IO) { val deviceContext = Context("Device${it}") { plugin(DeviceManager) } @@ -76,7 +77,7 @@ fun main() { deviceManager.install("device$it", MassDevice) val endpointId = "device$it" - val deviceEndpoint = MagixEndpoint.rSocketStreamWithTcp("localhost") + val deviceEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost") deviceManager.connectToMagix(deviceEndpoint, endpointId) } } @@ -90,21 +91,21 @@ fun main() { title = "Latest event" } bar { - launch(Dispatchers.Default){ - val monitorEndpoint = MagixEndpoint.rSocketStreamWithTcp("localhost") + launch(Dispatchers.IO) { + val monitorEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost") - val latest = ConcurrentHashMap() + val latest = ConcurrentHashMap() monitorEndpoint.subscribe(controlsMagixFormat).onEach { (magixMessage, payload) -> - latest[magixMessage.origin] = payload.time ?: Clock.System.now() + latest[magixMessage.origin] = Clock.System.now() - payload.time!! }.launchIn(this) while (isActive) { delay(200) - val now = Clock.System.now() val sorted = latest.mapKeys { it.key.substring(6).toInt() }.toSortedMap() + latest.clear() x.numbers = sorted.keys - y.numbers = sorted.values.map { now.minus(it).inWholeMilliseconds / 1000.0 } + y.numbers = sorted.values.map { it.inWholeMilliseconds / 1000.0 + 0.0001 } } } } 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 93f9f7c..0328631 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 @@ -40,16 +40,12 @@ fun D.fxProperty(spec: WritableDevicePropertySpec): init { //Read incoming changes onPropertyChange(spec) { - if (it != null) { - runLater { - try { - set(it) - } catch (ex: Throwable) { - logger.info { "Failed to set property $name to $it" } - } + runLater { + try { + set(it) + } catch (ex: Throwable) { + logger.info { "Failed to set property $name to $it" } } - } else { - invalidated() } } diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFlowPlugin.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFlowPlugin.kt index 8cf9ccd..83c95cc 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFlowPlugin.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFlowPlugin.kt @@ -2,8 +2,28 @@ package space.kscience.magix.api import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow +/** + * A plugin that could be inserted into basic loop implementation. + */ public fun interface MagixFlowPlugin { - public fun start(scope: CoroutineScope, magixFlow: MutableSharedFlow): Job + + /** + * Attach a [Job] to magix loop. + * Receive messages from [receive]. + * Send messages via [sendMessage] + */ + public fun start( + scope: CoroutineScope, + receive: Flow, + sendMessage: suspend (MagixMessage) -> Unit, + ): Job + + /** + * Use the same [MutableSharedFlow] to send and receive messages. Could be a bottleneck in case of many plugins. + */ + public fun start(scope: CoroutineScope, magixFlow: MutableSharedFlow): Job = + start(scope, magixFlow) { magixFlow.emit(it) } } \ No newline at end of file diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/connections/magixPortal.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/connections/magixPortal.kt new file mode 100644 index 0000000..9ede63f --- /dev/null +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/connections/magixPortal.kt @@ -0,0 +1,30 @@ +package space.kscience.magix.connections + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixMessageFilter + +/** + * Create a gateway between two magix endpoints using filters for forward and backward message passing. + * Portal is useful to create segmented magix loops: + * * limit the load on given loop segment by filtering some messages; + * * use different loop implementations. + */ +public fun CoroutineScope.launchMagixPortal( + firstEndpoint: MagixEndpoint, + secondEndpoint: MagixEndpoint, + forwardFilter: MagixMessageFilter = MagixMessageFilter.ALL, + backwardFilter: MagixMessageFilter = MagixMessageFilter.ALL, +): Job = launch { + firstEndpoint.subscribe(forwardFilter).onEach { + secondEndpoint.broadcast(it) + }.launchIn(this) + + secondEndpoint.subscribe(backwardFilter).onEach { + firstEndpoint.broadcast(it) + }.launchIn(this) +} diff --git a/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketMagixEndpoint.kt b/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketMagixEndpoint.kt index 19cc8f8..d3f5a2d 100644 --- a/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketMagixEndpoint.kt +++ b/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketMagixEndpoint.kt @@ -10,7 +10,10 @@ import io.rsocket.kotlin.ktor.client.RSocketSupport import io.rsocket.kotlin.ktor.client.rSocket import io.rsocket.kotlin.payload.buildPayload import io.rsocket.kotlin.payload.data -import kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map @@ -18,7 +21,6 @@ import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixMessage import space.kscience.magix.api.MagixMessageFilter import space.kscience.magix.api.filter -import kotlin.coroutines.coroutineContext public class RSocketMagixEndpoint(private val rSocket: RSocket) : MagixEndpoint, Closeable { @@ -34,7 +36,7 @@ public class RSocketMagixEndpoint(private val rSocket: RSocket) : MagixEndpoint, }.filter(filter).flowOn(rSocket.coroutineContext[CoroutineDispatcher] ?: Dispatchers.Unconfined) } - override suspend fun broadcast(message: MagixMessage): Unit = withContext(coroutineContext) { + override suspend fun broadcast(message: MagixMessage): Unit { val payload = buildPayload { data(MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message)) } diff --git a/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketStreamMagixEndpoint.kt b/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketStreamMagixEndpoint.kt index e4eae79..025036e 100644 --- a/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketStreamMagixEndpoint.kt +++ b/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketStreamMagixEndpoint.kt @@ -10,20 +10,16 @@ import io.rsocket.kotlin.ktor.client.rSocket import io.rsocket.kotlin.payload.Payload import io.rsocket.kotlin.payload.buildPayload import io.rsocket.kotlin.payload.data -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.cancel +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.map import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixMessage import space.kscience.magix.api.MagixMessageFilter import space.kscience.magix.api.filter -import kotlin.coroutines.CoroutineContext -import kotlin.coroutines.coroutineContext /** * RSocket endpoint based on an established channel. This way it works a lot faster than [RSocketMagixEndpoint] @@ -33,11 +29,10 @@ import kotlin.coroutines.coroutineContext */ public class RSocketStreamMagixEndpoint( private val rSocket: RSocket, - private val coroutineContext: CoroutineContext, public val streamFilter: MagixMessageFilter = MagixMessageFilter(), ) : MagixEndpoint, Closeable { - private val output: MutableSharedFlow = MutableSharedFlow() + private val output: Channel = Channel() private val input: Flow by lazy { rSocket.requestChannel( @@ -49,24 +44,22 @@ public class RSocketStreamMagixEndpoint( ) ) }, - output.map { message -> - buildPayload { - data(MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message)) - } - }.flowOn(coroutineContext[CoroutineDispatcher] ?: Dispatchers.Unconfined) + output.consumeAsFlow() ) } override fun subscribe( filter: MagixMessageFilter, - ): Flow { - return input.map { - MagixEndpoint.magixJson.decodeFromString(MagixMessage.serializer(), it.data.readText()) - }.filter(filter).flowOn(coroutineContext[CoroutineDispatcher] ?: Dispatchers.Unconfined) - } + ): Flow = input.map { + MagixEndpoint.magixJson.decodeFromString(MagixMessage.serializer(), it.data.readText()) + }.filter(filter) override suspend fun broadcast(message: MagixMessage): Unit { - output.emit(message) + output.send( + buildPayload { + data(MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message)) + } + ) } override fun close() { @@ -95,5 +88,5 @@ public suspend fun MagixEndpoint.Companion.rSocketStreamWithWebSockets( client.close() } - return RSocketStreamMagixEndpoint(rSocket, coroutineContext, filter) + return RSocketStreamMagixEndpoint(rSocket, filter) } \ No newline at end of file diff --git a/magix/magix-rsocket/src/jvmMain/kotlin/space/kscience/magix/rsocket/withTcp.kt b/magix/magix-rsocket/src/jvmMain/kotlin/space/kscience/magix/rsocket/withTcp.kt index 9dc0abd..02a6c9b 100644 --- a/magix/magix-rsocket/src/jvmMain/kotlin/space/kscience/magix/rsocket/withTcp.kt +++ b/magix/magix-rsocket/src/jvmMain/kotlin/space/kscience/magix/rsocket/withTcp.kt @@ -20,6 +20,7 @@ public suspend fun MagixEndpoint.Companion.rSocketWithTcp( val transport = TcpClientTransport( hostname = host, port = port, + context = coroutineContext, configure = tcpConfig ) val rSocket = buildConnector(rSocketConfig).connect(transport) @@ -38,9 +39,10 @@ public suspend fun MagixEndpoint.Companion.rSocketStreamWithTcp( val transport = TcpClientTransport( hostname = host, port = port, + context = coroutineContext, configure = tcpConfig ) val rSocket = buildConnector(rSocketConfig).connect(transport) - return RSocketStreamMagixEndpoint(rSocket, coroutineContext, filter) + return RSocketStreamMagixEndpoint(rSocket, filter) } \ No newline at end of file diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt index 3116513..84c9c75 100644 --- a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt @@ -1,8 +1,10 @@ package space.kscience.magix.server +import io.ktor.network.sockets.SocketOptions import io.rsocket.kotlin.ConnectionAcceptor import io.rsocket.kotlin.RSocketRequestHandler import io.rsocket.kotlin.core.RSocketServer +import io.rsocket.kotlin.core.RSocketServerBuilder import io.rsocket.kotlin.payload.Payload import io.rsocket.kotlin.payload.buildPayload import io.rsocket.kotlin.payload.data @@ -10,18 +12,32 @@ import io.rsocket.kotlin.transport.ktor.tcp.TcpServer import io.rsocket.kotlin.transport.ktor.tcp.TcpServerTransport import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.serialization.encodeToString import space.kscience.magix.api.* import space.kscience.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_RAW_PORT /** - * Raw TCP magix server + * Raw TCP magix server plugin */ -public class RSocketMagixFlowPlugin(public val port: Int = DEFAULT_MAGIX_RAW_PORT): MagixFlowPlugin { - override fun start(scope: CoroutineScope, magixFlow: MutableSharedFlow): Job { - val tcpTransport = TcpServerTransport(port = port) - val rSocketJob: TcpServer = RSocketServer().bindIn(scope, tcpTransport, acceptor(scope, magixFlow)) +public class RSocketMagixFlowPlugin( + private val serverHost: String = "0.0.0.0", + private val serverPort: Int = DEFAULT_MAGIX_RAW_PORT, + private val transportConfiguration: SocketOptions.AcceptorOptions.() -> Unit = {}, + private val rsocketConfiguration: RSocketServerBuilder.() -> Unit = {}, +) : MagixFlowPlugin { + + override fun start( + scope: CoroutineScope, + receive: Flow, + sendMessage: suspend (MagixMessage) -> Unit, + ): Job { + val tcpTransport = TcpServerTransport(hostname = serverHost, port = serverPort, configure = transportConfiguration) + val rSocketJob: TcpServer = RSocketServer(rsocketConfiguration) + .bindIn(scope, tcpTransport, acceptor(scope, receive, sendMessage)) scope.coroutineContext[Job]?.invokeOnCompletion { rSocketJob.handlerJob.cancel() @@ -30,40 +46,50 @@ public class RSocketMagixFlowPlugin(public val port: Int = DEFAULT_MAGIX_RAW_POR return rSocketJob.handlerJob } - public companion object{ + public companion object { public fun acceptor( coroutineScope: CoroutineScope, - magixFlow: MutableSharedFlow, + receive: Flow, + sendMessage: suspend (MagixMessage) -> Unit, ): ConnectionAcceptor = ConnectionAcceptor { RSocketRequestHandler(coroutineScope.coroutineContext) { //handler for request/stream requestStream { request: Payload -> - val filter = MagixEndpoint.magixJson.decodeFromString(MagixMessageFilter.serializer(), request.data.readText()) - magixFlow.filter(filter).map { message -> + val filter = MagixEndpoint.magixJson.decodeFromString( + MagixMessageFilter.serializer(), + request.data.readText() + ) + receive.filter(filter).map { message -> val string = MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message) buildPayload { data(string) } } } //single send fireAndForget { request: Payload -> - val message = MagixEndpoint.magixJson.decodeFromString(MagixMessage.serializer(), request.data.readText()) - magixFlow.emit(message) + val message = + MagixEndpoint.magixJson.decodeFromString(MagixMessage.serializer(), request.data.readText()) + sendMessage(message) } // bidirectional connection, used for streaming connection requestChannel { request: Payload, input: Flow -> input.onEach { - magixFlow.emit(MagixEndpoint.magixJson.decodeFromString(MagixMessage.serializer(), it.data.readText())) + sendMessage( + MagixEndpoint.magixJson.decodeFromString( + MagixMessage.serializer(), + it.data.readText() + ) + ) }.launchIn(this) val filterText = request.data.readText() - val filter = if(filterText.isNotBlank()){ + val filter = if (filterText.isNotBlank()) { MagixEndpoint.magixJson.decodeFromString(MagixMessageFilter.serializer(), filterText) } else { MagixMessageFilter() } - magixFlow.filter(filter).map { message -> + receive.filter(filter).map { message -> val string = MagixEndpoint.magixJson.encodeToString(message) buildPayload { data(string) } } diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt index e5cb6cb..d257bb7 100644 --- a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt @@ -104,8 +104,11 @@ public fun Application.magixModule(magixFlow: MutableSharedFlow, r val message = call.receive() magixFlow.emit(message) } - //rSocket server. Filter from Payload - rSocket("rsocket", acceptor = RSocketMagixFlowPlugin.acceptor( application, magixFlow)) + //rSocket WS server. Filter from Payload + rSocket( + "rsocket", + acceptor = RSocketMagixFlowPlugin.acceptor(application, magixFlow) { magixFlow.emit(it) } + ) } } } diff --git a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt index e878ce9..23715a7 100644 --- a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt +++ b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt @@ -23,7 +23,6 @@ public class ZmqMagixEndpoint( ) : MagixEndpoint, AutoCloseable { private val zmqContext by lazy { ZContext() } - @OptIn(ExperimentalCoroutinesApi::class) override fun subscribe(filter: MagixMessageFilter): Flow { val socket = zmqContext.createSocket(SocketType.SUB) socket.connect("$protocol://$host:$pubPort") diff --git a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt index e66d326..fa4f54a 100644 --- a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt +++ b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt @@ -1,7 +1,7 @@ package space.kscince.magix.zmq import kotlinx.coroutines.* -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.serialization.decodeFromString @@ -19,7 +19,12 @@ public class ZmqMagixFlowPlugin( public val zmqPubSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT, public val zmqPullSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, ) : MagixFlowPlugin { - override fun start(scope: CoroutineScope, magixFlow: MutableSharedFlow): Job = + + override fun start( + scope: CoroutineScope, + receive: Flow, + sendMessage: suspend (MagixMessage) -> Unit, + ): Job = scope.launch(Dispatchers.IO) { val logger = LoggerFactory.getLogger("magix-server-zmq") @@ -27,7 +32,7 @@ public class ZmqMagixFlowPlugin( //launch the publishing job val pubSocket = context.createSocket(SocketType.PUB) pubSocket.bind("$localHost:$zmqPubSocketPort") - magixFlow.onEach { message -> + receive.onEach { message -> val string = MagixEndpoint.magixJson.encodeToString(message) pubSocket.send(string) logger.trace("Published: $string") @@ -43,7 +48,7 @@ public class ZmqMagixFlowPlugin( if (string != null) { logger.trace("Received: $string") val message = MagixEndpoint.magixJson.decodeFromString(string) - magixFlow.emit(message) + sendMessage(message) } } } From 6f5270ee377f7fe4c8913f738728f4ca5eae7f69 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 23 Jul 2023 14:29:11 +0300 Subject: [PATCH 26/43] Add MQTT topic specialization --- .../ksceince/magix/mqtt/MqttMagixEndpoint.kt | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt b/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt index 66cf299..d8294dd 100644 --- a/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt +++ b/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt @@ -3,6 +3,7 @@ package space.ksceince.magix.mqtt import com.hivemq.client.mqtt.MqttClient import com.hivemq.client.mqtt.datatypes.MqttQos import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient +import com.hivemq.client.mqtt.mqtt5.Mqtt5ClientBuilder import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.trySendBlocking import kotlinx.coroutines.flow.Flow @@ -13,13 +14,21 @@ import space.kscience.magix.api.MagixMessage import space.kscience.magix.api.MagixMessageFilter import java.util.* - +`/** + * MQTT5 endpoint for magix. + * + * @param broadcastTopicBuilder defines how the topic is constructed from broadcast message structure. + * By default, use `magix/` topic if target is present and `magix` if it is not. + * @param subscribeTopicBuilder defines how the topic is constructed from the filter. + * By default, uses `magix/` if only a single target is presented and `magix/#` otherwise. + */ public class MqttMagixEndpoint( serverHost: String, clientId: String = UUID.randomUUID().toString(), - private val broadcastTopicBuilder: (MagixMessage) -> String = { DEFAULT_MAGIX_TOPIC_NAME }, - private val subscribeTopicBuilder: (MagixMessageFilter) -> String = { DEFAULT_MAGIX_TOPIC_NAME }, - public val qos: MqttQos = MqttQos.AT_LEAST_ONCE, + private val broadcastTopicBuilder: (MagixMessage) -> String = defaultBroadcastTopicBuilder, + private val subscribeTopicBuilder: (MagixMessageFilter) -> String = defaultSubscribeTopicBuilder, + private val qos: MqttQos = MqttQos.AT_LEAST_ONCE, + private val clientConfig: Mqtt5ClientBuilder.() -> Mqtt5ClientBuilder = { this }, ) : MagixEndpoint, AutoCloseable { private val client: Mqtt5AsyncClient by lazy { @@ -27,6 +36,7 @@ public class MqttMagixEndpoint( .identifier(clientId) .serverHost(serverHost) .useMqttVersion5() + .run(clientConfig) .buildAsync() } @@ -66,5 +76,18 @@ public class MqttMagixEndpoint( public companion object { public const val DEFAULT_MAGIX_TOPIC_NAME: String = "magix" + + + internal val defaultBroadcastTopicBuilder: (MagixMessage) -> String = { message -> + message.target?.let { "$DEFAULT_MAGIX_TOPIC_NAME/it" } ?: DEFAULT_MAGIX_TOPIC_NAME + } + + internal val defaultSubscribeTopicBuilder: (MagixMessageFilter) -> String = { filter -> + if (filter.target?.size == 1) { + "$DEFAULT_MAGIX_TOPIC_NAME/${filter.target!!.first()}" + } else { + "$DEFAULT_MAGIX_TOPIC_NAME/#" + } + } } } \ No newline at end of file From 405bcd6ba31a0ee55e250419a1686bfe516476ba Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 23 Jul 2023 14:29:26 +0300 Subject: [PATCH 27/43] Add ZMQ context customization --- .../kscince/magix/zmq/ZmqMagixEndpoint.kt | 3 +- .../kscince/magix/zmq/ZmqMagixFlowPlugin.kt | 47 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt index 23715a7..376208d 100644 --- a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt +++ b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt @@ -20,8 +20,9 @@ public class ZmqMagixEndpoint( private val pubPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT, private val pullPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, private val coroutineContext: CoroutineContext = Dispatchers.IO, + private val zmqContext: ZContext = ZContext() + ) : MagixEndpoint, AutoCloseable { - private val zmqContext by lazy { ZContext() } override fun subscribe(filter: MagixMessageFilter): Flow { val socket = zmqContext.createSocket(SocketType.SUB) diff --git a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt index fa4f54a..c4ec16a 100644 --- a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt +++ b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt @@ -24,35 +24,34 @@ public class ZmqMagixFlowPlugin( scope: CoroutineScope, receive: Flow, sendMessage: suspend (MagixMessage) -> Unit, - ): Job = - scope.launch(Dispatchers.IO) { - val logger = LoggerFactory.getLogger("magix-server-zmq") + ): Job = scope.launch(Dispatchers.IO) { + val logger = LoggerFactory.getLogger("magix-server-zmq") - ZContext().use { context -> - //launch the publishing job - val pubSocket = context.createSocket(SocketType.PUB) - pubSocket.bind("$localHost:$zmqPubSocketPort") - receive.onEach { message -> - val string = MagixEndpoint.magixJson.encodeToString(message) - pubSocket.send(string) - logger.trace("Published: $string") - }.launchIn(this) + ZContext().use { context -> + //launch the publishing job + val pubSocket = context.createSocket(SocketType.PUB) + pubSocket.bind("$localHost:$zmqPubSocketPort") + receive.onEach { message -> + val string = MagixEndpoint.magixJson.encodeToString(message) + pubSocket.send(string) + logger.trace("Published: $string") + }.launchIn(this) - //launch pulling job - val pullSocket = context.createSocket(SocketType.PULL) - pullSocket.bind("$localHost:$zmqPullSocketPort") - pullSocket.receiveTimeOut = 500 - //suspending loop while pulling is active - while (isActive) { - val string: String? = pullSocket.recvStr() - if (string != null) { - logger.trace("Received: $string") - val message = MagixEndpoint.magixJson.decodeFromString(string) - sendMessage(message) - } + //launch pulling job + val pullSocket = context.createSocket(SocketType.PULL) + pullSocket.bind("$localHost:$zmqPullSocketPort") + pullSocket.receiveTimeOut = 500 + //suspending loop while pulling is active + while (isActive) { + val string: String? = pullSocket.recvStr() + if (string != null) { + logger.trace("Received: $string") + val message = MagixEndpoint.magixJson.decodeFromString(string) + sendMessage(message) } } } + } } \ No newline at end of file From fcabd9aed458dbad28fc8e8629ba3c23caa25ee9 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 29 Jul 2023 09:45:34 +0300 Subject: [PATCH 28/43] Documentation update and minor refactoring --- .../kscience/controls/client/DeviceClient.kt | 5 +++-- .../kscience/controls/client/controlsMagix.kt | 10 ++++++++-- .../kscience/controls/demo/demoDeviceServer.kt | 5 +++-- .../controls/demo/car/MagixVirtualCar.kt | 5 +++-- .../space/kscience/controls/demo/MassDevice.kt | 4 ++-- .../space/kscience/magix/api/MagixFormat.kt | 18 ++++++++++++++++-- .../kscience/magix/api/MagixMessageFilter.kt | 9 ++++++--- .../ksceince/magix/mqtt/MqttMagixEndpoint.kt | 1 + .../magix/server/RSocketMagixFlowPlugin.kt | 17 +++++++++++------ .../space/kscience/magix/server/magixModule.kt | 16 ++++++++-------- settings.gradle.kts | 3 +-- 11 files changed, 62 insertions(+), 31 deletions(-) diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt index 699a766..a724fd6 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt @@ -6,6 +6,7 @@ import kotlinx.coroutines.newCoroutineContext import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import space.kscience.controls.api.* +import space.kscience.controls.manager.DeviceManager import space.kscience.dataforge.context.Context import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.misc.DFExperimental @@ -105,8 +106,8 @@ public class DeviceClient( * Connect to a remote device via this client. */ public fun MagixEndpoint.remoteDevice(context: Context, magixTarget: String, deviceName: Name): DeviceClient { - val subscription = subscribe(controlsMagixFormat, originFilter = listOf(magixTarget)).map { it.second } + val subscription = subscribe(DeviceManager.magixFormat, originFilter = listOf(magixTarget)).map { it.second } return DeviceClient(context, deviceName, subscription) { - broadcast(controlsMagixFormat, it, magixTarget, id = stringUID()) + broadcast(DeviceManager.magixFormat, it, magixTarget, id = stringUID()) } } \ No newline at end of file diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt index f64c8e0..00a1cc6 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt @@ -14,15 +14,20 @@ import space.kscience.dataforge.context.logger import space.kscience.magix.api.* -public val controlsMagixFormat: MagixFormat = MagixFormat( +internal val controlsMagixFormat: MagixFormat = MagixFormat( DeviceMessage.serializer(), setOf("controls-kt", "dataforge") ) +/** + * A magix message format to work with Controls-kt data + */ +public val DeviceManager.Companion.magixFormat: MagixFormat get() = controlsMagixFormat + internal fun generateId(request: MagixMessage): String = if (request.id != null) { "${request.id}.response" } else { - "df[${request.payload.hashCode()}" + "controls[${request.payload.hashCode().toString(16)}" } /** @@ -37,6 +42,7 @@ public fun DeviceManager.connectToMagix( if (responsePayload != null) { endpoint.broadcast( format = controlsMagixFormat, + target = request.origin, origin = endpointID, payload = responsePayload, id = generateId(request), diff --git a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt index 9ef69cf..fda8ead 100644 --- a/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt +++ b/demo/all-things/src/main/kotlin/space/kscience/controls/demo/demoDeviceServer.kt @@ -7,7 +7,8 @@ import kotlinx.coroutines.launch import kotlinx.html.div import kotlinx.html.link import space.kscience.controls.api.PropertyChangedMessage -import space.kscience.controls.client.controlsMagixFormat +import space.kscience.controls.client.magixFormat +import space.kscience.controls.manager.DeviceManager import space.kscience.controls.spec.name import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.get @@ -54,7 +55,7 @@ suspend fun Trace.updateXYFrom(flow: Flow>>) { fun CoroutineScope.startDemoDeviceServer(magixEndpoint: MagixEndpoint): ApplicationEngine { //share subscription to a parse message only once - val subscription = magixEndpoint.subscribe(controlsMagixFormat).shareIn(this, SharingStarted.Lazily) + val subscription = magixEndpoint.subscribe(DeviceManager.magixFormat).shareIn(this, SharingStarted.Lazily) val sinFlow = subscription.mapNotNull { (_, payload) -> (payload as? PropertyChangedMessage)?.takeIf { it.property == DemoDevice.sin.name } 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 addc3a9..03c781b 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 @@ -2,7 +2,8 @@ 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.client.magixFormat +import space.kscience.controls.manager.DeviceManager import space.kscience.controls.spec.write import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Factory @@ -18,7 +19,7 @@ import kotlin.time.ExperimentalTime class MagixVirtualCar(context: Context, meta: Meta) : VirtualCar(context, meta) { private fun MagixEndpoint.launchMagixVirtualCarUpdate() = launch { - subscribe(controlsMagixFormat).collect { (_, payload) -> + subscribe(DeviceManager.magixFormat).collect { (_, payload) -> (payload as? PropertyChangedMessage)?.let { message -> if (message.sourceDevice == Name.parse("virtual-car")) { when (message.property) { diff --git a/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt b/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt index 6dae782..5860211 100644 --- a/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt +++ b/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt @@ -8,7 +8,7 @@ import kotlinx.coroutines.isActive import kotlinx.coroutines.launch import kotlinx.datetime.Clock import space.kscience.controls.client.connectToMagix -import space.kscience.controls.client.controlsMagixFormat +import space.kscience.controls.client.magixFormat import space.kscience.controls.manager.DeviceManager import space.kscience.controls.manager.install import space.kscience.controls.spec.* @@ -96,7 +96,7 @@ fun main() { val latest = ConcurrentHashMap() - monitorEndpoint.subscribe(controlsMagixFormat).onEach { (magixMessage, payload) -> + monitorEndpoint.subscribe(DeviceManager.magixFormat).onEach { (magixMessage, payload) -> latest[magixMessage.origin] = Clock.System.now() - payload.time!! }.launchIn(this) diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt index 4921129..904afe6 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt @@ -6,6 +6,11 @@ import kotlinx.serialization.KSerializer import kotlinx.serialization.json.JsonElement import space.kscience.magix.api.MagixEndpoint.Companion.magixJson +/** + * A format for [MagixMessage] that allows to decode typed payload + * + * @param formats allowed values of the format field that are processed. The first value is the primary format for sending. + */ public data class MagixFormat( val serializer: KSerializer, val formats: Set, @@ -13,10 +18,15 @@ public data class MagixFormat( val defaultFormat: String get() = formats.firstOrNull() ?: "magix" } +/** + * Subscribe for messages in given endpoint using [format] to declare format filter as well as automatic decoding. + * + * @return a flow of pairs (raw message, decoded message). Raw messages are to be used to extract headers. + */ public fun MagixEndpoint.subscribe( format: MagixFormat, - originFilter: Collection? = null, - targetFilter: Collection? = null, + originFilter: Collection? = null, + targetFilter: Collection? = null, ): Flow> = subscribe( MagixMessageFilter(format = format.formats, origin = originFilter, target = targetFilter) ).map { @@ -24,6 +34,10 @@ public fun MagixEndpoint.subscribe( it to value } +/** + * Send a message using given [format] to encode the message payload. The format field is also taken from [format]. + * + */ public suspend fun MagixEndpoint.broadcast( format: MagixFormat, payload: T, diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt index 4ce1995..10d4345 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt @@ -4,11 +4,14 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter import kotlinx.serialization.Serializable +/** + * A filter that allows receiving only messages with format, origin and target in given list. + */ @Serializable public data class MagixMessageFilter( - val format: Collection? = null, - val origin: Collection? = null, - val target: Collection? = null, + val format: Collection? = null, + val origin: Collection? = null, + val target: Collection? = null, ) { public fun accepts(message: MagixMessage): Boolean = diff --git a/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt b/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt index d8294dd..b5f7c0a 100644 --- a/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt +++ b/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt @@ -77,6 +77,7 @@ public class MqttMagixEndpoint( public companion object { public const val DEFAULT_MAGIX_TOPIC_NAME: String = "magix" + //TODO add target name escaping internal val defaultBroadcastTopicBuilder: (MagixMessage) -> String = { message -> message.target?.let { "$DEFAULT_MAGIX_TOPIC_NAME/it" } ?: DEFAULT_MAGIX_TOPIC_NAME diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt index 84c9c75..9eb9ad6 100644 --- a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt @@ -35,7 +35,8 @@ public class RSocketMagixFlowPlugin( receive: Flow, sendMessage: suspend (MagixMessage) -> Unit, ): Job { - val tcpTransport = TcpServerTransport(hostname = serverHost, port = serverPort, configure = transportConfiguration) + val tcpTransport = + TcpServerTransport(hostname = serverHost, port = serverPort, configure = transportConfiguration) val rSocketJob: TcpServer = RSocketServer(rsocketConfiguration) .bindIn(scope, tcpTransport, acceptor(scope, receive, sendMessage)) @@ -59,6 +60,7 @@ public class RSocketMagixFlowPlugin( MagixMessageFilter.serializer(), request.data.readText() ) + request.close() receive.filter(filter).map { message -> val string = MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message) buildPayload { data(string) } @@ -66,22 +68,25 @@ public class RSocketMagixFlowPlugin( } //single send fireAndForget { request: Payload -> - val message = - MagixEndpoint.magixJson.decodeFromString(MagixMessage.serializer(), request.data.readText()) + val message = MagixEndpoint.magixJson.decodeFromString( + MagixMessage.serializer(), + request.data.readText() + ) + request.close() sendMessage(message) } // bidirectional connection, used for streaming connection requestChannel { request: Payload, input: Flow -> - input.onEach { + input.onEach { inputPayload -> sendMessage( MagixEndpoint.magixJson.decodeFromString( MagixMessage.serializer(), - it.data.readText() + inputPayload.use{ it.data.readText()} ) ) }.launchIn(this) - val filterText = request.data.readText() + val filterText = request.use { it.data.readText()} val filter = if (filterText.isNotBlank()) { MagixEndpoint.magixJson.decodeFromString(MagixMessageFilter.serializer(), filterText) diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt index d257bb7..dc197ad 100644 --- a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/magixModule.kt @@ -47,24 +47,24 @@ public fun Application.magixModule(magixFlow: MutableSharedFlow, r install(WebSockets) } + if (pluginOrNull(RSocketSupport) == null) { + install(RSocketSupport) + } + + // if (pluginOrNull(CORS) == null) { // install(CORS) { // //TODO consider more safe policy // anyHost() // } // } - if (pluginOrNull(ContentNegotiation) == null) { - install(ContentNegotiation) { - json() - } - } - if (pluginOrNull(RSocketSupport) == null) { - install(RSocketSupport) - } routing { route(route) { + install(ContentNegotiation){ + json() + } get("state") { call.respondHtml { head { diff --git a/settings.gradle.kts b/settings.gradle.kts index 4fee116..db083c4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -58,8 +58,7 @@ include( ":magix:magix-zmq", ":magix:magix-rabbit", ":magix:magix-mqtt", - -// ":magix:magix-storage", + ":magix:magix-storage", ":magix:magix-storage:magix-storage-xodus", ":controls-magix-client", ":demo:all-things", From 870cb7ef4064e5e367ef81c47de3a651ae5b5e0f Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 29 Jul 2023 09:46:13 +0300 Subject: [PATCH 29/43] [WIP] Storage API --- .../ksceince/magix/mqtt/MqttMagixEndpoint.kt | 2 +- magix/magix-storage/build.gradle.kts | 22 +++++ .../magix-storage-xodus/build.gradle.kts | 7 +- .../magix/storage/xodus/XodusMagixStorage.kt | 83 ++++++++++++++++-- .../kscience/magix/storage/MagixHistory.kt | 85 +++++++++++++++++++ .../magix/storage/MagixHistoryPayload.kt | 44 ++++++++++ .../kscience/magix/storage/historyEndpoint.kt | 82 ++++++++++++++++++ 7 files changed, 314 insertions(+), 11 deletions(-) create mode 100644 magix/magix-storage/build.gradle.kts create mode 100644 magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt create mode 100644 magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistoryPayload.kt create mode 100644 magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt diff --git a/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt b/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt index b5f7c0a..296ddb0 100644 --- a/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt +++ b/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt @@ -14,7 +14,7 @@ import space.kscience.magix.api.MagixMessage import space.kscience.magix.api.MagixMessageFilter import java.util.* -`/** +/** * MQTT5 endpoint for magix. * * @param broadcastTopicBuilder defines how the topic is constructed from broadcast message structure. diff --git a/magix/magix-storage/build.gradle.kts b/magix/magix-storage/build.gradle.kts new file mode 100644 index 0000000..43306c4 --- /dev/null +++ b/magix/magix-storage/build.gradle.kts @@ -0,0 +1,22 @@ +plugins { + id("space.kscience.gradle.mpp") + `maven-publish` +} + +description = """ + Magix history database API +""".trimIndent() + + +kscience { + jvm() + js() + native() + useSerialization { + json() + } + dependencies { + api(projects.magix.magixApi) + api(spclibs.kotlinx.datetime) + } +} diff --git a/magix/magix-storage/magix-storage-xodus/build.gradle.kts b/magix/magix-storage/magix-storage-xodus/build.gradle.kts index 8869867..ed1dc32 100644 --- a/magix/magix-storage/magix-storage-xodus/build.gradle.kts +++ b/magix/magix-storage/magix-storage-xodus/build.gradle.kts @@ -5,17 +5,18 @@ plugins { val xodusVersion: String by rootProject.extra -kscience{ +kscience { useCoroutines() } dependencies { - api(projects.magix.magixApi) + api(projects.magix.magixStorage) implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") +// implementation("org.jetbrains.xodus:dnq:2.0.0") testImplementation(spclibs.kotlinx.coroutines.test) } -readme{ +readme { maturity = space.kscience.gradle.Maturity.PROTOTYPE } diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt index 39eb4ea..1c9e636 100644 --- a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt @@ -1,25 +1,30 @@ package space.kscience.magix.storage.xodus -import jetbrains.exodus.entitystore.Entity -import jetbrains.exodus.entitystore.PersistentEntityStore -import jetbrains.exodus.entitystore.PersistentEntityStores +import jetbrains.exodus.entitystore.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.* import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixEndpoint.Companion.magixJson import space.kscience.magix.api.MagixMessage import space.kscience.magix.api.MagixMessageFilter +import space.kscience.magix.storage.MagixHistory +import space.kscience.magix.storage.MagixPayloadFilter +import space.kscience.magix.storage.MagixUsernameFilter import java.nio.file.Path +import kotlin.sequences.Sequence +/** + * Attach a Xodus storage process to the given endpoint. + */ public class XodusMagixStorage( scope: CoroutineScope, private val store: PersistentEntityStore, endpoint: MagixEndpoint, - filter: MagixMessageFilter = MagixMessageFilter(), -) : AutoCloseable { + filter: MagixMessageFilter = MagixMessageFilter.ALL, +) : MagixHistory, AutoCloseable { //TODO consider message buffering internal val subscriptionJob = endpoint.subscribe(filter).onEach { message -> @@ -40,7 +45,14 @@ public class XodusMagixStorage( setProperty(MagixMessage::parentId.name, it) } message.user?.let { - setBlobString(MagixMessage::user.name, MagixEndpoint.magixJson.encodeToString(it)) + setProperty( + MagixMessage::user.name, + when (it) { + is JsonObject -> it["name"]?.jsonPrimitive?.content ?: "@error" + is JsonPrimitive -> it.content + else -> "@error" + } + ) } } } @@ -60,6 +72,10 @@ public class XodusMagixStorage( }, ) + + /** + * Access all messages in a given format + */ public fun readByFormat( format: String, block: (Sequence) -> Unit, @@ -74,6 +90,9 @@ public class XodusMagixStorage( block(sequence) } + /** + * Access all messages as + */ public fun readAll( block: (Sequence) -> Unit, ): Unit = store.executeInReadonlyTransaction { transaction -> @@ -83,6 +102,56 @@ public class XodusMagixStorage( block(sequence) } + override suspend fun findMessages( + magixFilter: MagixMessageFilter?, + payloadFilters: List, + userFilter: MagixUsernameFilter?, + callback: (Sequence) -> Unit, + ): Unit = store.executeInReadonlyTransaction { transaction -> + val all = transaction.getAll(MAGIC_MESSAGE_ENTITY_TYPE) + + fun StoreTransaction.findAllIn( + entityType: String, + field: String, + values: Collection?, + ): EntityIterable? { + var union: EntityIterable? = null + values?.forEach { + val filter = transaction.find(entityType, field, it) + union = union?.union(filter) ?: filter + } + return union + } + + // filter by magix filter + val filteredByMagix: EntityIterable = magixFilter?.let { mf -> + var res = all + transaction.findAllIn(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::format.name, mf.format)?.let { + res = res.intersect(it) + } + transaction.findAllIn(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::origin.name, mf.origin)?.let { + res = res.intersect(it) + } + transaction.findAllIn(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::target.name, mf.target)?.let { + res = res.intersect(it) + } + + res + } ?: all + + val filteredByUser: EntityIterable = userFilter?.let { userFilter-> + filteredByMagix.intersect( + transaction.find(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::user.name, userFilter.userName) + ) + } ?: filteredByMagix + + + filteredByUser.se + + + block(sequence) + } + override fun close() { subscriptionJob.cancel() } diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt new file mode 100644 index 0000000..615fb95 --- /dev/null +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt @@ -0,0 +1,85 @@ +package space.kscience.magix.storage + +import kotlinx.datetime.LocalDateTime +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement +import space.kscience.magix.api.MagixFormat +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter +import kotlin.jvm.JvmInline + +@Serializable +public sealed class MagixPayloadFilter { + @SerialName("eq") + public class Equals(public val path: String, public val value: JsonElement) : MagixPayloadFilter() + + @SerialName("not") + public class Not(public val argument: MagixPayloadFilter) : MagixPayloadFilter() + + +// @SerialName("like") +// public class Like(public val path: String, public val value: String) : MagixPayloadFilter() + + @SerialName("numberInRange") + public class NumberInRange(public val path: String, public val from: Number, public val to: Number) : + MagixPayloadFilter() + + @SerialName("dateTimeInRange") + public class DateTimeInRange( + public val path: String, + public val from: LocalDateTime, + public val to: LocalDateTime, + ) : MagixPayloadFilter() +} + +public fun MagixPayloadFilter.test(element: JsonElement): Boolean { + TODO() +// when (this) { +// is MagixPayloadFilter.DateTimeInRange -> TODO() +// is MagixPayloadFilter.Equals -> TODO() +// is MagixPayloadFilter.Not -> !(argument.test(element)) +// is MagixPayloadFilter.NumberInRange -> element.jsonObject[path] +// } +} + +public fun Sequence.filter(magixPayloadFilter: MagixPayloadFilter): Sequence = filter { + magixPayloadFilter.test(it) +} + + +@Serializable +@JvmInline +public value class MagixUsernameFilter(public val userName: String) + +/** + * An interface for history access to magix messages + */ +public interface MagixHistory { + /** + * Find messages using intersection of given filters. If filters are not defined, get all messages. + * + * The result is supplied as a callback with [Sequence] of messages. If backing storage uses transactions, the function + * closes all transactions after use. + * + * @param magixFilter magix header filter. + * @param payloadFilters filters for payload fields. + * @param userFilter filters user names ("user.name"). + */ + public suspend fun findMessages( + magixFilter: MagixMessageFilter? = null, + payloadFilters: List = emptyList(), + userFilter: MagixUsernameFilter? = null, + callback: (Sequence) -> Unit, + ) + + public companion object { + public const val HISTORY_PAYLOAD_FORMAT: String = "magix.history" + + public val magixFormat: MagixFormat = MagixFormat( + MagixHistoryPayload.serializer(), + setOf(HISTORY_PAYLOAD_FORMAT) + ) + } +} + diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistoryPayload.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistoryPayload.kt new file mode 100644 index 0000000..a2af500 --- /dev/null +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistoryPayload.kt @@ -0,0 +1,44 @@ +package space.kscience.magix.storage + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter + +/** + * Base class for history API request and response messages + */ +@Serializable +public sealed class MagixHistoryPayload + +/** + * Message to request history information from the storage + * + * @param magixFilter filter for magix headers + * @param payloadFilters filter for payload fields + * @param userFilter filter for user name + * @param pageSize if defined, defines the maximum number of messages per response message. If not defined, uses history provider default. + */ +@Serializable +@SerialName("history.request") +public data class HistoryRequestPayload( + val magixFilter: MagixMessageFilter? = null, + val payloadFilters: List = emptyList(), + val userFilter: MagixUsernameFilter? = null, + val pageSize: Int? = null +) : MagixHistoryPayload() + +/** + * A response to a [HistoryRequestPayload]. Contains a list of messages. + * + * @param messages the list of messages. + * @param page the index of current page for multiple page messages. Page indexing starts with 0. + * @param lastPage true if this page is the last. + */ +@Serializable +@SerialName("history.response") +public data class HistoryResponsePayload( + val messages: List, + val page: Int = 0, + val lastPage: Boolean = true +) : MagixHistoryPayload() \ No newline at end of file diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt new file mode 100644 index 0000000..cfee042 --- /dev/null +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt @@ -0,0 +1,82 @@ +package space.kscience.magix.storage + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.serialization.json.JsonObject +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.broadcast +import space.kscience.magix.api.subscribe + +internal fun generateId(request: MagixMessage): String = if (request.id != null) { + "${request.id}.response" +} else { + "history[${request.payload.hashCode().toString(16)}" +} + +/** + * Launch responding history messages on this [MagixEndpoint]. The process does not store messages, only responds to history requests. + * + * @param scope the [CoroutineScope] in which the responding process runs. + * @param history the history database. + * @param targetFilter filters the request messages by target if defined. + * @param pageSize maximum messages per page in the response. The default is 100. + * @param user user block for outgoing messages if defined. + * @param origin tag for outgoing messages if defined. + */ +public fun MagixEndpoint.launchHistory( + scope: CoroutineScope, + history: MagixHistory, + targetFilter: Collection? = null, + pageSize: Int = 100, + user: JsonObject? = null, + origin: String = MagixHistory.HISTORY_PAYLOAD_FORMAT, +): Job = subscribe(MagixHistory.magixFormat, targetFilter = targetFilter).onEach { (request, payload) -> + + fun send(chunk: List, pageNumber: Int, end: Boolean) { + scope.launch { + val sendPayload = HistoryResponsePayload( + chunk, + pageNumber + ) + broadcast( + format = MagixHistory.magixFormat, + payload = sendPayload, + target = request.origin, + id = generateId(request), + parentId = request.id, + user = user, + origin = origin, + ) + } + } + + + if (payload is HistoryRequestPayload) { + val realPageSize = payload.pageSize ?: pageSize + history.findMessages(payload.magixFilter, payload.payloadFilters, payload.userFilter) { sequence -> + // start from -1 because increment always happens first + var pageNumber = -1 + + //remember the last chunk to determine which is last + var chunk: List? = null + + sequence.chunked(realPageSize).forEach { + //If the last chunk was not final, send it + chunk?.let { chunk -> + send(chunk, pageNumber, false) + } + pageNumber++ + // update last chunk + chunk = it + } + // send the final chunk + chunk?.let { + send(it, pageNumber, true) + } + } + } +}.launchIn(scope) \ No newline at end of file From cf70339a9f2b5443ef95e16878d0290f03cde2df Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 29 Jul 2023 09:47:59 +0300 Subject: [PATCH 30/43] Rename field in MagixMessage in accordance with https://github.com/waltz-controls/rfc/issues/12 --- .../kscience/controls/client/controlsMagix.kt | 2 +- .../space/kscience/controls/demo/echo/main.kt | 6 +++--- .../space/kscience/controls/demo/MassDevice.kt | 2 +- .../kotlin/space/kscience/magix/api/MagixFormat.kt | 4 ++-- .../space/kscience/magix/api/MagixMessage.kt | 4 ++-- .../space/kscience/magix/api/MagixMessageFilter.kt | 4 ++-- .../kotlin/space/kscience/magix/api/converters.kt | 4 ++-- .../space/ksceince/magix/mqtt/MqttMagixEndpoint.kt | 2 +- .../magix/storage/xodus/XodusMagixStorage.kt | 14 +++++++------- .../kscience/magix/storage/historyEndpoint.kt | 2 +- 10 files changed, 22 insertions(+), 22 deletions(-) diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt index 00a1cc6..af223e3 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt @@ -42,7 +42,7 @@ public fun DeviceManager.connectToMagix( if (responsePayload != null) { endpoint.broadcast( format = controlsMagixFormat, - target = request.origin, + target = request.sourceEndpoint, origin = endpointID, payload = responsePayload, id = generateId(request), diff --git a/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt b/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt index f172b9a..881997b 100644 --- a/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt +++ b/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt @@ -44,8 +44,8 @@ private suspend fun MagixEndpoint.collectEcho(scope: CoroutineScope, n: Int) { MagixMessage( format = "test", payload = JsonObject(emptyMap()), - origin = "test", - target = "loop", + sourceEndpoint = "test", + targetEndpoint = "loop", id = it.toString() ) ) @@ -65,7 +65,7 @@ suspend fun main(): Unit = coroutineScope { //echo each message flow.onEach { message -> if (message.parentId == null) { - val m = message.copy(origin = "loop", parentId = message.id, id = message.id + ".response") + val m = message.copy(sourceEndpoint = "loop", parentId = message.id, id = message.id + ".response") logger.info(m.toString()) send(m) } diff --git a/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt b/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt index 5860211..5014a6f 100644 --- a/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt +++ b/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt @@ -97,7 +97,7 @@ fun main() { val latest = ConcurrentHashMap() monitorEndpoint.subscribe(DeviceManager.magixFormat).onEach { (magixMessage, payload) -> - latest[magixMessage.origin] = Clock.System.now() - payload.time!! + latest[magixMessage.sourceEndpoint] = Clock.System.now() - payload.time!! }.launchIn(this) while (isActive) { diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt index 904afe6..c68e326 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt @@ -50,8 +50,8 @@ public suspend fun MagixEndpoint.broadcast( val message = MagixMessage( format = format.defaultFormat, payload = magixJson.encodeToJsonElement(format.serializer, payload), - origin = origin, - target = target, + sourceEndpoint = origin, + targetEndpoint = target, id = id, parentId = parentId, user = user diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessage.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessage.kt index c25c08a..4162593 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessage.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessage.kt @@ -27,8 +27,8 @@ import kotlinx.serialization.json.JsonElement public data class MagixMessage( val format: String, val payload: JsonElement, - val origin: String, - val target: String? = null, + val sourceEndpoint: String, + val targetEndpoint: String? = null, val id: String? = null, val parentId: String? = null, val user: JsonElement? = null, diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt index 10d4345..12def5b 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt @@ -16,8 +16,8 @@ public data class MagixMessageFilter( public fun accepts(message: MagixMessage): Boolean = format?.contains(message.format) ?: true - && origin?.contains(message.origin) ?: true - && target?.contains(message.target) ?: true + && origin?.contains(message.sourceEndpoint) ?: true + && target?.contains(message.targetEndpoint) ?: true public companion object { public val ALL: MagixMessageFilter = MagixMessageFilter() diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/converters.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/converters.kt index 441e141..beed930 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/converters.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/converters.kt @@ -20,8 +20,8 @@ public fun CoroutineScope.launchMagixConverter( val transformed: MagixMessage = MagixMessage( outputFormat, newPayload, - newOrigin ?: message.origin, - message.target, + newOrigin ?: message.sourceEndpoint, + message.targetEndpoint, message.id, message.parentId, message.user diff --git a/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt b/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt index 296ddb0..ae41532 100644 --- a/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt +++ b/magix/magix-mqtt/src/main/kotlin/space/ksceince/magix/mqtt/MqttMagixEndpoint.kt @@ -80,7 +80,7 @@ public class MqttMagixEndpoint( //TODO add target name escaping internal val defaultBroadcastTopicBuilder: (MagixMessage) -> String = { message -> - message.target?.let { "$DEFAULT_MAGIX_TOPIC_NAME/it" } ?: DEFAULT_MAGIX_TOPIC_NAME + message.targetEndpoint?.let { "$DEFAULT_MAGIX_TOPIC_NAME/it" } ?: DEFAULT_MAGIX_TOPIC_NAME } internal val defaultSubscribeTopicBuilder: (MagixMessageFilter) -> String = { filter -> diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt index 1c9e636..7a8ead1 100644 --- a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt @@ -30,13 +30,13 @@ public class XodusMagixStorage( internal val subscriptionJob = endpoint.subscribe(filter).onEach { message -> store.executeInTransaction { transaction -> transaction.newEntity(MAGIC_MESSAGE_ENTITY_TYPE).apply { - setProperty(MagixMessage::origin.name, message.origin) + setProperty(MagixMessage::sourceEndpoint.name, message.sourceEndpoint) setProperty(MagixMessage::format.name, message.format) setBlobString(MagixMessage::payload.name, MagixEndpoint.magixJson.encodeToString(message.payload)) - message.target?.let { - setProperty(MagixMessage::target.name, it) + message.targetEndpoint?.let { + setProperty(MagixMessage::targetEndpoint.name, it) } message.id?.let { setProperty(MagixMessage::id.name, it) @@ -63,8 +63,8 @@ public class XodusMagixStorage( payload = getBlobString(MagixMessage::payload.name)?.let { magixJson.parseToJsonElement(it) } ?: JsonObject(emptyMap()), - origin = getProperty(MagixMessage::origin.name).toString(), - target = getProperty(MagixMessage::target.name)?.toString(), + sourceEndpoint = getProperty(MagixMessage::sourceEndpoint.name).toString(), + targetEndpoint = getProperty(MagixMessage::targetEndpoint.name)?.toString(), id = getProperty(MagixMessage::id.name)?.toString(), parentId = getProperty(MagixMessage::parentId.name)?.toString(), user = getBlobString(MagixMessage::user.name)?.let { @@ -129,10 +129,10 @@ public class XodusMagixStorage( transaction.findAllIn(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::format.name, mf.format)?.let { res = res.intersect(it) } - transaction.findAllIn(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::origin.name, mf.origin)?.let { + transaction.findAllIn(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::sourceEndpoint.name, mf.origin)?.let { res = res.intersect(it) } - transaction.findAllIn(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::target.name, mf.target)?.let { + transaction.findAllIn(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::targetEndpoint.name, mf.target)?.let { res = res.intersect(it) } diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt index cfee042..45a7f81 100644 --- a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt @@ -45,7 +45,7 @@ public fun MagixEndpoint.launchHistory( broadcast( format = MagixHistory.magixFormat, payload = sendPayload, - target = request.origin, + target = request.sourceEndpoint, id = generateId(request), parentId = request.id, user = user, From f9e20f87666f325e8acdaa15ea8aa807ac8db58a Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 29 Jul 2023 11:52:04 +0300 Subject: [PATCH 31/43] Make it buildable again --- .../magix/storage/xodus/XodusMagixStorage.kt | 22 ++++++++++++----- .../kscience/magix/storage/MagixHistory.kt | 24 +++++++++---------- .../magix/storage/MagixHistoryPayload.kt | 2 +- .../kscience/magix/storage/historyEndpoint.kt | 2 +- magix/magix-zmq/build.gradle.kts | 2 +- .../kscince/magix/zmq/ZmqMagixEndpoint.kt | 1 - 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt index 7a8ead1..24d35be 100644 --- a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt @@ -5,7 +5,9 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.* +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.jsonPrimitive import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixEndpoint.Companion.magixJson import space.kscience.magix.api.MagixMessage @@ -13,6 +15,7 @@ import space.kscience.magix.api.MagixMessageFilter import space.kscience.magix.storage.MagixHistory import space.kscience.magix.storage.MagixPayloadFilter import space.kscience.magix.storage.MagixUsernameFilter +import space.kscience.magix.storage.test import java.nio.file.Path import kotlin.sequences.Sequence @@ -33,7 +36,7 @@ public class XodusMagixStorage( setProperty(MagixMessage::sourceEndpoint.name, message.sourceEndpoint) setProperty(MagixMessage::format.name, message.format) - setBlobString(MagixMessage::payload.name, MagixEndpoint.magixJson.encodeToString(message.payload)) + setBlobString(MagixMessage::payload.name, magixJson.encodeToString(message.payload)) message.targetEndpoint?.let { setProperty(MagixMessage::targetEndpoint.name, it) @@ -104,7 +107,7 @@ public class XodusMagixStorage( override suspend fun findMessages( magixFilter: MagixMessageFilter?, - payloadFilters: List, + payloadFilter: MagixPayloadFilter?, userFilter: MagixUsernameFilter?, callback: (Sequence) -> Unit, ): Unit = store.executeInReadonlyTransaction { transaction -> @@ -139,17 +142,24 @@ public class XodusMagixStorage( res } ?: all - val filteredByUser: EntityIterable = userFilter?.let { userFilter-> + val filteredByUser: EntityIterable = userFilter?.let { userFilter -> filteredByMagix.intersect( transaction.find(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::user.name, userFilter.userName) ) } ?: filteredByMagix - filteredByUser.se + val sequence = filteredByUser.asSequence().map { it.parseMagixMessage() } + val filteredSequence = if (payloadFilter == null) { + sequence + } else { + sequence.filter { + payloadFilter.test(it.payload) + } + } - block(sequence) + callback(filteredSequence) } override fun close() { diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt index 615fb95..c64ffc1 100644 --- a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt @@ -14,10 +14,6 @@ public sealed class MagixPayloadFilter { @SerialName("eq") public class Equals(public val path: String, public val value: JsonElement) : MagixPayloadFilter() - @SerialName("not") - public class Not(public val argument: MagixPayloadFilter) : MagixPayloadFilter() - - // @SerialName("like") // public class Like(public val path: String, public val value: String) : MagixPayloadFilter() @@ -31,16 +27,20 @@ public sealed class MagixPayloadFilter { public val from: LocalDateTime, public val to: LocalDateTime, ) : MagixPayloadFilter() + + + @SerialName("not") + public class Not(public val argument: MagixPayloadFilter) : MagixPayloadFilter() + + @SerialName("and") + public class And(public val left: MagixPayloadFilter, public val right: MagixPayloadFilter) : MagixPayloadFilter() + + @SerialName("or") + public class Or(public val left: MagixPayloadFilter, public val right: MagixPayloadFilter) : MagixPayloadFilter() } public fun MagixPayloadFilter.test(element: JsonElement): Boolean { TODO() -// when (this) { -// is MagixPayloadFilter.DateTimeInRange -> TODO() -// is MagixPayloadFilter.Equals -> TODO() -// is MagixPayloadFilter.Not -> !(argument.test(element)) -// is MagixPayloadFilter.NumberInRange -> element.jsonObject[path] -// } } public fun Sequence.filter(magixPayloadFilter: MagixPayloadFilter): Sequence = filter { @@ -63,12 +63,12 @@ public interface MagixHistory { * closes all transactions after use. * * @param magixFilter magix header filter. - * @param payloadFilters filters for payload fields. + * @param payloadFilter filter for payload fields. * @param userFilter filters user names ("user.name"). */ public suspend fun findMessages( magixFilter: MagixMessageFilter? = null, - payloadFilters: List = emptyList(), + payloadFilter: MagixPayloadFilter? = null, userFilter: MagixUsernameFilter? = null, callback: (Sequence) -> Unit, ) diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistoryPayload.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistoryPayload.kt index a2af500..5438fa4 100644 --- a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistoryPayload.kt +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistoryPayload.kt @@ -23,7 +23,7 @@ public sealed class MagixHistoryPayload @SerialName("history.request") public data class HistoryRequestPayload( val magixFilter: MagixMessageFilter? = null, - val payloadFilters: List = emptyList(), + val payloadFilter: MagixPayloadFilter? = null, val userFilter: MagixUsernameFilter? = null, val pageSize: Int? = null ) : MagixHistoryPayload() diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt index 45a7f81..cbd5fd4 100644 --- a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt @@ -57,7 +57,7 @@ public fun MagixEndpoint.launchHistory( if (payload is HistoryRequestPayload) { val realPageSize = payload.pageSize ?: pageSize - history.findMessages(payload.magixFilter, payload.payloadFilters, payload.userFilter) { sequence -> + history.findMessages(payload.magixFilter, payload.payloadFilter, payload.userFilter) { sequence -> // start from -1 because increment always happens first var pageNumber = -1 diff --git a/magix/magix-zmq/build.gradle.kts b/magix/magix-zmq/build.gradle.kts index e7c9f77..3258e17 100644 --- a/magix/magix-zmq/build.gradle.kts +++ b/magix/magix-zmq/build.gradle.kts @@ -10,5 +10,5 @@ description = """ dependencies { api(projects.magix.magixApi) api("org.slf4j:slf4j-api:2.0.6") - implementation("org.zeromq:jeromq:0.5.2") + api("org.zeromq:jeromq:0.5.2") } diff --git a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt index 376208d..2e9ab8b 100644 --- a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt +++ b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt @@ -21,7 +21,6 @@ public class ZmqMagixEndpoint( private val pullPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, private val coroutineContext: CoroutineContext = Dispatchers.IO, private val zmqContext: ZContext = ZContext() - ) : MagixEndpoint, AutoCloseable { override fun subscribe(filter: MagixMessageFilter): Flow { From d3d841383780474cd694150c79d8a05621c0b44c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sat, 29 Jul 2023 18:08:25 +0300 Subject: [PATCH 32/43] Add storage API test (inclomplete) --- build.gradle.kts | 2 +- .../kscience/controls/api/DeviceMessage.kt | 70 +++--- .../space/kscience/controls/demo/echo/main.kt | 2 +- demo/many-devices/build.gradle.kts | 2 +- .../kscience/controls/demo/MassDevice.kt | 36 +-- .../space/kscience/magix/api/MagixFormat.kt | 2 +- .../kscience/magix/api/MagixMessageFilter.kt | 4 +- .../magix/rsocket/RSocketMagixEndpoint.kt | 13 +- .../magix/server/RSocketMagixFlowPlugin.kt | 15 +- .../magix/storage/xodus/XodusMagixStorage.kt | 227 ++++++++++-------- .../magix/storage/xodus/storageAPITest.kt | 50 ++++ .../kscience/magix/storage/MagixHistory.kt | 2 +- .../kscience/magix/storage/historyEndpoint.kt | 2 +- .../kscince/magix/zmq/ZmqMagixEndpoint.kt | 6 +- .../kscince/magix/zmq/ZmqMagixFlowPlugin.kt | 3 +- 15 files changed, 264 insertions(+), 172 deletions(-) create mode 100644 magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/storageAPITest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 93a689c..8034b20 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,7 +6,7 @@ plugins { id("space.kscience.gradle.project") } -val dataforgeVersion: String by extra("0.6.1") +val dataforgeVersion: String by extra("0.6.2-dev-3") val ktorVersion: String by extra(space.kscience.gradle.KScienceVersions.ktorVersion) val rsocketVersion by extra("0.15.4") val xodusVersion by extra("2.0.1") diff --git a/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt index 0b4131e..45d7f0b 100644 --- a/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt +++ b/controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt @@ -1,7 +1,11 @@ +@file:OptIn(ExperimentalSerializationApi::class) + package space.kscience.controls.api import kotlinx.datetime.Clock import kotlinx.datetime.Instant +import kotlinx.serialization.EncodeDefault +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @@ -55,9 +59,9 @@ public data class PropertyChangedMessage( override val sourceDevice: Name = Name.EMPTY, override val targetDevice: Name? = null, override val comment: String? = null, - override val time: Instant? = Clock.System.now() -) : DeviceMessage(){ - override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = block(sourceDevice)) + @EncodeDefault override val time: Instant? = Clock.System.now(), +) : DeviceMessage() { + override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = block(sourceDevice)) } /** @@ -71,9 +75,9 @@ public data class PropertySetMessage( override val sourceDevice: Name? = null, override val targetDevice: Name, override val comment: String? = null, - override val time: Instant? = Clock.System.now() -) : DeviceMessage(){ - override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) + @EncodeDefault override val time: Instant? = Clock.System.now(), +) : DeviceMessage() { + override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) } /** @@ -87,9 +91,9 @@ public data class PropertyGetMessage( override val sourceDevice: Name? = null, override val targetDevice: Name, override val comment: String? = null, - override val time: Instant? = Clock.System.now() -) : DeviceMessage(){ - override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) + @EncodeDefault override val time: Instant? = Clock.System.now(), +) : DeviceMessage() { + override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) } /** @@ -101,9 +105,9 @@ public data class GetDescriptionMessage( override val sourceDevice: Name? = null, override val targetDevice: Name, override val comment: String? = null, - override val time: Instant? = Clock.System.now() -) : DeviceMessage(){ - override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) + @EncodeDefault override val time: Instant? = Clock.System.now(), +) : DeviceMessage() { + override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) } /** @@ -118,9 +122,9 @@ public data class DescriptionMessage( override val sourceDevice: Name, override val targetDevice: Name? = null, override val comment: String? = null, - override val time: Instant? = Clock.System.now() -) : DeviceMessage(){ - override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = block(sourceDevice)) + @EncodeDefault override val time: Instant? = Clock.System.now(), +) : DeviceMessage() { + override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = block(sourceDevice)) } /** @@ -137,9 +141,9 @@ public data class ActionExecuteMessage( override val sourceDevice: Name? = null, override val targetDevice: Name, override val comment: String? = null, - override val time: Instant? = Clock.System.now() -) : DeviceMessage(){ - override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) + @EncodeDefault override val time: Instant? = Clock.System.now(), +) : DeviceMessage() { + override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) } /** @@ -156,9 +160,9 @@ public data class ActionResultMessage( override val sourceDevice: Name, override val targetDevice: Name? = null, override val comment: String? = null, - override val time: Instant? = Clock.System.now() -) : DeviceMessage(){ - override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = block(sourceDevice)) + @EncodeDefault override val time: Instant? = Clock.System.now(), +) : DeviceMessage() { + override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = block(sourceDevice)) } /** @@ -171,9 +175,9 @@ public data class BinaryNotificationMessage( override val sourceDevice: Name, override val targetDevice: Name? = null, override val comment: String? = null, - override val time: Instant? = Clock.System.now() -) : DeviceMessage(){ - override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = block(sourceDevice)) + @EncodeDefault override val time: Instant? = Clock.System.now(), +) : DeviceMessage() { + override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = block(sourceDevice)) } /** @@ -186,9 +190,9 @@ public data class EmptyDeviceMessage( override val sourceDevice: Name? = null, override val targetDevice: Name? = null, override val comment: String? = null, - override val time: Instant? = Clock.System.now() -) : DeviceMessage(){ - override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) + @EncodeDefault override val time: Instant? = Clock.System.now(), +) : DeviceMessage() { + override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) } /** @@ -202,9 +206,9 @@ public data class DeviceLogMessage( override val sourceDevice: Name? = null, override val targetDevice: Name? = null, override val comment: String? = null, - override val time: Instant? = Clock.System.now() -) : DeviceMessage(){ - override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) + @EncodeDefault override val time: Instant? = Clock.System.now(), +) : DeviceMessage() { + override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) } /** @@ -219,9 +223,9 @@ public data class DeviceErrorMessage( override val sourceDevice: Name, override val targetDevice: Name? = null, override val comment: String? = null, - override val time: Instant? = Clock.System.now() -) : DeviceMessage(){ - override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = block(sourceDevice)) + @EncodeDefault override val time: Instant? = Clock.System.now(), +) : DeviceMessage() { + override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = block(sourceDevice)) } diff --git a/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt b/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt index 881997b..c0ad995 100644 --- a/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt +++ b/demo/echo/src/main/kotlin/space/kscience/controls/demo/echo/main.kt @@ -22,7 +22,7 @@ private suspend fun MagixEndpoint.collectEcho(scope: CoroutineScope, n: Int) { scope.launch { subscribe( MagixMessageFilter( - origin = listOf("loop") + source = listOf("loop") ) ).collect { message -> if (message.id?.endsWith(".response") == true) { diff --git a/demo/many-devices/build.gradle.kts b/demo/many-devices/build.gradle.kts index 8c765d6..277d47b 100644 --- a/demo/many-devices/build.gradle.kts +++ b/demo/many-devices/build.gradle.kts @@ -19,7 +19,7 @@ dependencies { implementation(projects.magix.magixZmq) implementation("io.ktor:ktor-client-cio:$ktorVersion") - implementation("space.kscience:plotlykt-server:0.5.3") + implementation("space.kscience:plotlykt-server:0.6.0") implementation(spclibs.logback.classic) } diff --git a/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt b/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt index 5014a6f..56b66b9 100644 --- a/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt +++ b/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt @@ -1,11 +1,10 @@ package space.kscience.controls.demo -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay +import kotlinx.coroutines.* import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.datetime.Clock import space.kscience.controls.client.connectToMagix import space.kscience.controls.client.magixFormat @@ -20,6 +19,7 @@ import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.int import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.subscribe +import space.kscience.magix.rsocket.rSocketWithTcp import space.kscience.magix.rsocket.rSocketWithWebSockets import space.kscience.magix.server.RSocketMagixFlowPlugin import space.kscience.magix.server.startMagixServer @@ -31,7 +31,6 @@ import space.kscience.plotly.server.PlotlyUpdateMode import space.kscience.plotly.server.serve import space.kscience.plotly.server.show import space.kscince.magix.zmq.ZmqMagixFlowPlugin -import java.util.concurrent.ConcurrentHashMap import kotlin.random.Random import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds @@ -49,14 +48,15 @@ class MassDevice(context: Context, meta: Meta) : DeviceBySpec(MassDe val value by doubleProperty { randomValue } override suspend fun MassDevice.onOpen() { - doRecurring(50.milliseconds) { + doRecurring(10.milliseconds) { read(value) } } } } -fun main() { +@OptIn(DelicateCoroutinesApi::class) +suspend fun main() { val context = Context("Mass") context.startMagixServer( @@ -67,7 +67,7 @@ fun main() { val numDevices = 100 repeat(numDevices) { - context.launch(Dispatchers.IO) { + context.launch(newFixedThreadPoolContext(2, "Device${it}")) { val deviceContext = Context("Device${it}") { plugin(DeviceManager) } @@ -77,7 +77,7 @@ fun main() { deviceManager.install("device$it", MassDevice) val endpointId = "device$it" - val deviceEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost") + val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost") deviceManager.connectToMagix(deviceEndpoint, endpointId) } } @@ -94,18 +94,24 @@ fun main() { launch(Dispatchers.IO) { val monitorEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost") - val latest = ConcurrentHashMap() + val mutex = Mutex() + + val latest = HashMap() monitorEndpoint.subscribe(DeviceManager.magixFormat).onEach { (magixMessage, payload) -> - latest[magixMessage.sourceEndpoint] = Clock.System.now() - payload.time!! + mutex.withLock { + latest[magixMessage.sourceEndpoint] = Clock.System.now() - payload.time!! + } }.launchIn(this) while (isActive) { delay(200) - val sorted = latest.mapKeys { it.key.substring(6).toInt() }.toSortedMap() - latest.clear() - x.numbers = sorted.keys - y.numbers = sorted.values.map { it.inWholeMilliseconds / 1000.0 + 0.0001 } + mutex.withLock { + val sorted = latest.mapKeys { it.key.substring(6).toInt() }.toSortedMap() + latest.clear() + x.numbers = sorted.keys + y.numbers = sorted.values.map { it.inWholeMilliseconds / 1000.0 + 0.0001 } + } } } } diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt index c68e326..d1a02e8 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt @@ -28,7 +28,7 @@ public fun MagixEndpoint.subscribe( originFilter: Collection? = null, targetFilter: Collection? = null, ): Flow> = subscribe( - MagixMessageFilter(format = format.formats, origin = originFilter, target = targetFilter) + MagixMessageFilter(format = format.formats, source = originFilter, target = targetFilter) ).map { val value: T = magixJson.decodeFromJsonElement(format.serializer, it.payload) it to value diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt index 12def5b..4bd4746 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessageFilter.kt @@ -10,13 +10,13 @@ import kotlinx.serialization.Serializable @Serializable public data class MagixMessageFilter( val format: Collection? = null, - val origin: Collection? = null, + val source: Collection? = null, val target: Collection? = null, ) { public fun accepts(message: MagixMessage): Boolean = format?.contains(message.format) ?: true - && origin?.contains(message.sourceEndpoint) ?: true + && source?.contains(message.sourceEndpoint) ?: true && target?.contains(message.targetEndpoint) ?: true public companion object { diff --git a/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketMagixEndpoint.kt b/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketMagixEndpoint.kt index d3f5a2d..6c4e9dd 100644 --- a/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketMagixEndpoint.kt +++ b/magix/magix-rsocket/src/commonMain/kotlin/space/kscience/magix/rsocket/RSocketMagixEndpoint.kt @@ -36,7 +36,7 @@ public class RSocketMagixEndpoint(private val rSocket: RSocket) : MagixEndpoint, }.filter(filter).flowOn(rSocket.coroutineContext[CoroutineDispatcher] ?: Dispatchers.Unconfined) } - override suspend fun broadcast(message: MagixMessage): Unit { + override suspend fun broadcast(message: MagixMessage): Unit { val payload = buildPayload { data(MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message)) } @@ -51,11 +51,12 @@ public class RSocketMagixEndpoint(private val rSocket: RSocket) : MagixEndpoint, } -internal fun buildConnector(rSocketConfig: RSocketConnectorBuilder.ConnectionConfigBuilder.() -> Unit) = - RSocketConnector { - reconnectable(10) - connectionConfig(rSocketConfig) - } +internal fun buildConnector( + rSocketConfig: RSocketConnectorBuilder.ConnectionConfigBuilder.() -> Unit, +) = RSocketConnector { + reconnectable(5) + connectionConfig(rSocketConfig) +} /** * Build a websocket based endpoint connected to [host], [port] and given routing [path] diff --git a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt index 9eb9ad6..a00c33f 100644 --- a/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt +++ b/magix/magix-server/src/main/kotlin/space/kscience/magix/server/RSocketMagixFlowPlugin.kt @@ -35,8 +35,11 @@ public class RSocketMagixFlowPlugin( receive: Flow, sendMessage: suspend (MagixMessage) -> Unit, ): Job { - val tcpTransport = - TcpServerTransport(hostname = serverHost, port = serverPort, configure = transportConfiguration) + val tcpTransport = TcpServerTransport( + hostname = serverHost, + port = serverPort, + configure = transportConfiguration + ) val rSocketJob: TcpServer = RSocketServer(rsocketConfiguration) .bindIn(scope, tcpTransport, acceptor(scope, receive, sendMessage)) @@ -60,7 +63,7 @@ public class RSocketMagixFlowPlugin( MagixMessageFilter.serializer(), request.data.readText() ) - request.close() + receive.filter(filter).map { message -> val string = MagixEndpoint.magixJson.encodeToString(MagixMessage.serializer(), message) buildPayload { data(string) } @@ -72,7 +75,7 @@ public class RSocketMagixFlowPlugin( MagixMessage.serializer(), request.data.readText() ) - request.close() + sendMessage(message) } // bidirectional connection, used for streaming connection @@ -81,12 +84,12 @@ public class RSocketMagixFlowPlugin( sendMessage( MagixEndpoint.magixJson.decodeFromString( MagixMessage.serializer(), - inputPayload.use{ it.data.readText()} + inputPayload.use { it.data.readText() } ) ) }.launchIn(this) - val filterText = request.use { it.data.readText()} + val filterText = request.use { it.data.readText() } val filter = if (filterText.isNotBlank()) { MagixEndpoint.magixJson.decodeFromString(MagixMessageFilter.serializer(), filterText) diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt index 24d35be..9f28b9e 100644 --- a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt @@ -19,6 +19,129 @@ import space.kscience.magix.storage.test import java.nio.file.Path import kotlin.sequences.Sequence + +private fun Entity.parseMagixMessage(): MagixMessage = MagixMessage( + format = getProperty(MagixMessage::format.name).toString(), + payload = getBlobString(MagixMessage::payload.name)?.let { + magixJson.parseToJsonElement(it) + } ?: JsonObject(emptyMap()), + sourceEndpoint = getProperty(MagixMessage::sourceEndpoint.name).toString(), + targetEndpoint = getProperty(MagixMessage::targetEndpoint.name)?.toString(), + id = getProperty(MagixMessage::id.name)?.toString(), + parentId = getProperty(MagixMessage::parentId.name)?.toString(), + user = getBlobString(MagixMessage::user.name)?.let { + magixJson.parseToJsonElement(it) + }, +) + +public class XodusMagixHistory(private val store: PersistentEntityStore) : MagixHistory { + + public fun writeMessage(storeTransaction: StoreTransaction, message: MagixMessage) { + storeTransaction.newEntity(XodusMagixStorage.MAGIC_MESSAGE_ENTITY_TYPE).apply { + setProperty(MagixMessage::sourceEndpoint.name, message.sourceEndpoint) + setProperty(MagixMessage::format.name, message.format) + + setBlobString(MagixMessage::payload.name, magixJson.encodeToString(message.payload)) + + message.targetEndpoint?.let { + setProperty(MagixMessage::targetEndpoint.name, it) + } + message.id?.let { + setProperty(MagixMessage::id.name, it) + } + message.parentId?.let { + setProperty(MagixMessage::parentId.name, it) + } + message.user?.let { + setProperty( + MagixMessage::user.name, + when (it) { + is JsonObject -> it["name"]?.jsonPrimitive?.content ?: "@error" + is JsonPrimitive -> it.content + else -> "@error" + } + ) + } + } + } + + public fun sendMessage(message: MagixMessage) { + store.executeInTransaction { transaction -> + writeMessage(transaction, message) + } + } + + override suspend fun useMessages( + magixFilter: MagixMessageFilter?, + payloadFilter: MagixPayloadFilter?, + userFilter: MagixUsernameFilter?, + callback: (Sequence) -> Unit, + ): Unit = store.executeInReadonlyTransaction { transaction -> + val all = transaction.getAll(XodusMagixStorage.MAGIC_MESSAGE_ENTITY_TYPE) + + fun StoreTransaction.findAllIn( + entityType: String, + field: String, + values: Collection?, + ): EntityIterable? { + var union: EntityIterable? = null + values?.forEach { + val filter = transaction.find(entityType, field, it) + union = union?.union(filter) ?: filter + } + return union + } + + // filter by magix filter + val filteredByMagix: EntityIterable = magixFilter?.let { mf -> + var res = all + transaction.findAllIn(XodusMagixStorage.MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::format.name, mf.format) + ?.let { + res = res.intersect(it) + } + transaction.findAllIn( + XodusMagixStorage.MAGIC_MESSAGE_ENTITY_TYPE, + MagixMessage::sourceEndpoint.name, + mf.source + )?.let { + res = res.intersect(it) + } + transaction.findAllIn( + XodusMagixStorage.MAGIC_MESSAGE_ENTITY_TYPE, + MagixMessage::targetEndpoint.name, + mf.target + )?.let { + res = res.intersect(it) + } + + res + } ?: all + + val filteredByUser: EntityIterable = userFilter?.let { userFilter -> + filteredByMagix.intersect( + transaction.find( + XodusMagixStorage.MAGIC_MESSAGE_ENTITY_TYPE, + MagixMessage::user.name, + userFilter.userName + ) + ) + } ?: filteredByMagix + + + val sequence = filteredByUser.asSequence().map { it.parseMagixMessage() } + + val filteredSequence = if (payloadFilter == null) { + sequence + } else { + sequence.filter { + payloadFilter.test(it.payload) + } + } + + callback(filteredSequence) + } +} + /** * Attach a Xodus storage process to the given endpoint. */ @@ -27,54 +150,15 @@ public class XodusMagixStorage( private val store: PersistentEntityStore, endpoint: MagixEndpoint, filter: MagixMessageFilter = MagixMessageFilter.ALL, -) : MagixHistory, AutoCloseable { +) : AutoCloseable { + + public val history: XodusMagixHistory = XodusMagixHistory(store) //TODO consider message buffering internal val subscriptionJob = endpoint.subscribe(filter).onEach { message -> - store.executeInTransaction { transaction -> - transaction.newEntity(MAGIC_MESSAGE_ENTITY_TYPE).apply { - setProperty(MagixMessage::sourceEndpoint.name, message.sourceEndpoint) - setProperty(MagixMessage::format.name, message.format) - - setBlobString(MagixMessage::payload.name, magixJson.encodeToString(message.payload)) - - message.targetEndpoint?.let { - setProperty(MagixMessage::targetEndpoint.name, it) - } - message.id?.let { - setProperty(MagixMessage::id.name, it) - } - message.parentId?.let { - setProperty(MagixMessage::parentId.name, it) - } - message.user?.let { - setProperty( - MagixMessage::user.name, - when (it) { - is JsonObject -> it["name"]?.jsonPrimitive?.content ?: "@error" - is JsonPrimitive -> it.content - else -> "@error" - } - ) - } - } - } + history.sendMessage(message) }.launchIn(scope) - private fun Entity.parseMagixMessage(): MagixMessage = MagixMessage( - format = getProperty(MagixMessage::format.name).toString(), - payload = getBlobString(MagixMessage::payload.name)?.let { - magixJson.parseToJsonElement(it) - } ?: JsonObject(emptyMap()), - sourceEndpoint = getProperty(MagixMessage::sourceEndpoint.name).toString(), - targetEndpoint = getProperty(MagixMessage::targetEndpoint.name)?.toString(), - id = getProperty(MagixMessage::id.name)?.toString(), - parentId = getProperty(MagixMessage::parentId.name)?.toString(), - user = getBlobString(MagixMessage::user.name)?.let { - magixJson.parseToJsonElement(it) - }, - ) - /** * Access all messages in a given format @@ -105,63 +189,6 @@ public class XodusMagixStorage( block(sequence) } - override suspend fun findMessages( - magixFilter: MagixMessageFilter?, - payloadFilter: MagixPayloadFilter?, - userFilter: MagixUsernameFilter?, - callback: (Sequence) -> Unit, - ): Unit = store.executeInReadonlyTransaction { transaction -> - val all = transaction.getAll(MAGIC_MESSAGE_ENTITY_TYPE) - - fun StoreTransaction.findAllIn( - entityType: String, - field: String, - values: Collection?, - ): EntityIterable? { - var union: EntityIterable? = null - values?.forEach { - val filter = transaction.find(entityType, field, it) - union = union?.union(filter) ?: filter - } - return union - } - - // filter by magix filter - val filteredByMagix: EntityIterable = magixFilter?.let { mf -> - var res = all - transaction.findAllIn(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::format.name, mf.format)?.let { - res = res.intersect(it) - } - transaction.findAllIn(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::sourceEndpoint.name, mf.origin)?.let { - res = res.intersect(it) - } - transaction.findAllIn(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::targetEndpoint.name, mf.target)?.let { - res = res.intersect(it) - } - - res - } ?: all - - val filteredByUser: EntityIterable = userFilter?.let { userFilter -> - filteredByMagix.intersect( - transaction.find(MAGIC_MESSAGE_ENTITY_TYPE, MagixMessage::user.name, userFilter.userName) - ) - } ?: filteredByMagix - - - val sequence = filteredByUser.asSequence().map { it.parseMagixMessage() } - - val filteredSequence = if (payloadFilter == null) { - sequence - } else { - sequence.filter { - payloadFilter.test(it.payload) - } - } - - callback(filteredSequence) - } - override fun close() { subscriptionJob.cancel() } diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/storageAPITest.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/storageAPITest.kt new file mode 100644 index 0000000..dc1d42b --- /dev/null +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/storageAPITest.kt @@ -0,0 +1,50 @@ +package space.kscience.magix.storage.xodus + +import jetbrains.exodus.entitystore.PersistentEntityStores +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonObject +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter +import java.nio.file.Files +import kotlin.time.measureTime + +public suspend fun main() { + val storeDirectory = Files.createTempDirectory("controls-xodus").toFile() + println(storeDirectory) + val store = PersistentEntityStores.newInstance(storeDirectory) + val history = XodusMagixHistory(store) + + store.executeInTransaction { transaction -> + for (value in 1..100) { + for (source in 1..100) { + for (target in 1..100) { + history.writeMessage( + transaction, + MagixMessage( + "test", + sourceEndpoint = "source$source", + targetEndpoint = "target$target", + payload = buildJsonObject { + put("value", JsonPrimitive(value)) + } + ) + ) + } + } + } + } + + println("written million messages") + + + val time = measureTime { + history.useMessages( + MagixMessageFilter(source = listOf("source12"), target = listOf("target12")) + ) { sequence -> + println(sequence.count()) + } + } + println("Finished query in $time") + + store.close() +} \ No newline at end of file diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt index c64ffc1..e991fa9 100644 --- a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt @@ -66,7 +66,7 @@ public interface MagixHistory { * @param payloadFilter filter for payload fields. * @param userFilter filters user names ("user.name"). */ - public suspend fun findMessages( + public suspend fun useMessages( magixFilter: MagixMessageFilter? = null, payloadFilter: MagixPayloadFilter? = null, userFilter: MagixUsernameFilter? = null, diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt index cbd5fd4..4009f0b 100644 --- a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt @@ -57,7 +57,7 @@ public fun MagixEndpoint.launchHistory( if (payload is HistoryRequestPayload) { val realPageSize = payload.pageSize ?: pageSize - history.findMessages(payload.magixFilter, payload.payloadFilter, payload.userFilter) { sequence -> + history.useMessages(payload.magixFilter, payload.payloadFilter, payload.userFilter) { sequence -> // start from -1 because increment always happens first var pageNumber = -1 diff --git a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt index 2e9ab8b..7a8aeb7 100644 --- a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt +++ b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixEndpoint.kt @@ -13,6 +13,7 @@ import space.kscience.magix.api.MagixMessage import space.kscience.magix.api.MagixMessageFilter import space.kscience.magix.api.filter import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.coroutineContext public class ZmqMagixEndpoint( private val host: String, @@ -69,8 +70,7 @@ public class ZmqMagixEndpoint( } } -public fun MagixEndpoint.Companion.zmq( - scope: CoroutineScope, +public suspend fun MagixEndpoint.Companion.zmq( host: String, protocol: String = "tcp", pubPort: Int = DEFAULT_MAGIX_ZMQ_PUB_PORT, @@ -80,5 +80,5 @@ public fun MagixEndpoint.Companion.zmq( protocol, pubPort, pullPort, - coroutineContext = scope.coroutineContext + coroutineContext = coroutineContext ) \ No newline at end of file diff --git a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt index c4ec16a..7813b8c 100644 --- a/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt +++ b/magix/magix-zmq/src/main/kotlin/space/kscince/magix/zmq/ZmqMagixFlowPlugin.kt @@ -18,6 +18,7 @@ public class ZmqMagixFlowPlugin( public val localHost: String = "tcp://*", public val zmqPubSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PUB_PORT, public val zmqPullSocketPort: Int = MagixEndpoint.DEFAULT_MAGIX_ZMQ_PULL_PORT, + private val zContext: ZContext = ZContext() ) : MagixFlowPlugin { override fun start( @@ -27,7 +28,7 @@ public class ZmqMagixFlowPlugin( ): Job = scope.launch(Dispatchers.IO) { val logger = LoggerFactory.getLogger("magix-server-zmq") - ZContext().use { context -> + zContext.use { context -> //launch the publishing job val pubSocket = context.createSocket(SocketType.PUB) pubSocket.bind("$localHost:$zmqPubSocketPort") From dc2d0094fca7b7ef799ccafbfe609552341d5faa Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 30 Jul 2023 22:34:47 +0300 Subject: [PATCH 33/43] add filter implementation for Json --- .../kscience/controls/client/controlsMagix.kt | 2 +- .../magix/storage/xodus/XodusMagixStorage.kt | 18 +++++++------- .../kscience/magix/storage/MagixHistory.kt | 24 +++++++++++++++++-- .../kscience/magix/storage/historyEndpoint.kt | 17 ++++++++----- 4 files changed, 44 insertions(+), 17 deletions(-) diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt index af223e3..c129c79 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt @@ -16,7 +16,7 @@ import space.kscience.magix.api.* internal val controlsMagixFormat: MagixFormat = MagixFormat( DeviceMessage.serializer(), - setOf("controls-kt", "dataforge") + setOf("controls-kt") ) /** diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt index 9f28b9e..bfd7508 100644 --- a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt @@ -12,10 +12,7 @@ import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixEndpoint.Companion.magixJson import space.kscience.magix.api.MagixMessage import space.kscience.magix.api.MagixMessageFilter -import space.kscience.magix.storage.MagixHistory -import space.kscience.magix.storage.MagixPayloadFilter -import space.kscience.magix.storage.MagixUsernameFilter -import space.kscience.magix.storage.test +import space.kscience.magix.storage.* import java.nio.file.Path import kotlin.sequences.Sequence @@ -149,16 +146,19 @@ public class XodusMagixStorage( scope: CoroutineScope, private val store: PersistentEntityStore, endpoint: MagixEndpoint, - filter: MagixMessageFilter = MagixMessageFilter.ALL, + endpointName: String? = null, + subscriptionFilter: MagixMessageFilter = MagixMessageFilter.ALL, ) : AutoCloseable { public val history: XodusMagixHistory = XodusMagixHistory(store) //TODO consider message buffering - internal val subscriptionJob = endpoint.subscribe(filter).onEach { message -> + private val subscriptionJob = endpoint.subscribe(subscriptionFilter).onEach { message -> history.sendMessage(message) }.launchIn(scope) + private val broadcastJob = endpoint.launchHistory(scope, history, endpointName = endpointName) + /** * Access all messages in a given format @@ -204,14 +204,16 @@ public class XodusMagixStorage( public fun MagixEndpoint.storeInXodus( scope: CoroutineScope, xodusStore: PersistentEntityStore, + endpointName: String? = null, filter: MagixMessageFilter = MagixMessageFilter(), -): XodusMagixStorage = XodusMagixStorage(scope, xodusStore, this, filter) +): XodusMagixStorage = XodusMagixStorage(scope, xodusStore, this, endpointName, filter) public fun MagixEndpoint.storeInXodus( scope: CoroutineScope, path: Path, + endpointName: String? = null, filter: MagixMessageFilter = MagixMessageFilter(), ): XodusMagixStorage { val store = PersistentEntityStores.newInstance(path.toFile()) - return XodusMagixStorage(scope, store, this, filter) + return XodusMagixStorage(scope, store, this, endpointName, filter) } \ No newline at end of file diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt index e991fa9..3059481 100644 --- a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt @@ -4,6 +4,7 @@ import kotlinx.datetime.LocalDateTime import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.jsonObject import space.kscience.magix.api.MagixFormat import space.kscience.magix.api.MagixMessage import space.kscience.magix.api.MagixMessageFilter @@ -39,8 +40,27 @@ public sealed class MagixPayloadFilter { public class Or(public val left: MagixPayloadFilter, public val right: MagixPayloadFilter) : MagixPayloadFilter() } -public fun MagixPayloadFilter.test(element: JsonElement): Boolean { - TODO() +private fun JsonElement.takeElement(path: String): JsonElement? = if (path.isEmpty()) { + this +} else { + val separatorIndex = path.indexOf(".") + if (separatorIndex == -1) { + jsonObject[path] + } else { + val firstSegment = path.substring(0, separatorIndex) + val remaining = path.substring(separatorIndex + 1, path.length) + jsonObject[firstSegment]?.takeElement(remaining) + } + +} + +public fun MagixPayloadFilter.test(element: JsonElement): Boolean = when (this) { + is MagixPayloadFilter.Equals -> element.takeElement(path) == value + is MagixPayloadFilter.DateTimeInRange -> TODO() + is MagixPayloadFilter.NumberInRange -> TODO() + is MagixPayloadFilter.Not -> !argument.test(element) + is MagixPayloadFilter.And -> left.test(element) && right.test(element) + is MagixPayloadFilter.Or -> left.test(element) || right.test(element) } public fun Sequence.filter(magixPayloadFilter: MagixPayloadFilter): Sequence = filter { diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt index 4009f0b..67b2cd3 100644 --- a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt @@ -5,7 +5,8 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonPrimitive import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixMessage import space.kscience.magix.api.broadcast @@ -22,7 +23,7 @@ internal fun generateId(request: MagixMessage): String = if (request.id != null) * * @param scope the [CoroutineScope] in which the responding process runs. * @param history the history database. - * @param targetFilter filters the request messages by target if defined. + * @param endpointName the name of this endpoint that is used as a filter. * @param pageSize maximum messages per page in the response. The default is 100. * @param user user block for outgoing messages if defined. * @param origin tag for outgoing messages if defined. @@ -30,17 +31,21 @@ internal fun generateId(request: MagixMessage): String = if (request.id != null) public fun MagixEndpoint.launchHistory( scope: CoroutineScope, history: MagixHistory, - targetFilter: Collection? = null, + endpointName: String? = null, pageSize: Int = 100, - user: JsonObject? = null, + user: JsonElement? = endpointName?.let { JsonPrimitive(endpointName) }, origin: String = MagixHistory.HISTORY_PAYLOAD_FORMAT, -): Job = subscribe(MagixHistory.magixFormat, targetFilter = targetFilter).onEach { (request, payload) -> +): Job = subscribe( + MagixHistory.magixFormat, + targetFilter = endpointName?.let { setOf(it) } +).onEach { (request, payload) -> fun send(chunk: List, pageNumber: Int, end: Boolean) { scope.launch { val sendPayload = HistoryResponsePayload( chunk, - pageNumber + pageNumber, + lastPage = end ) broadcast( format = MagixHistory.magixFormat, From a172da0a3d61ac4a80b1859c3a0fb0149db3ffd8 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 3 Aug 2023 19:21:35 +0300 Subject: [PATCH 34/43] Add abstraction for writeable magix history --- .../space/kscience/magix/api/MagixMessage.kt | 15 +++++++-- magix/magix-storage/build.gradle.kts | 1 + .../magix/storage/xodus/XodusMagixStorage.kt | 21 ++++-------- .../kscience/magix/storage/MagixHistory.kt | 19 +++++++++-- .../commonTest/kotlin/InMemoryMagixHistory.kt | 32 +++++++++++++++++++ 5 files changed, 70 insertions(+), 18 deletions(-) create mode 100644 magix/magix-storage/src/commonTest/kotlin/InMemoryMagixHistory.kt diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessage.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessage.kt index 4162593..c1557e0 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessage.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixMessage.kt @@ -1,7 +1,7 @@ package space.kscience.magix.api import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.* /* @@ -32,4 +32,15 @@ public data class MagixMessage( val id: String? = null, val parentId: String? = null, val user: JsonElement? = null, -) \ No newline at end of file +) + +/** + * The default accessor for username. If `user` is an object, take it's "name" field. + * If it is primitive, take its content. Return "@error" if it is an array. + */ +public val MagixMessage.userName: String? get() = when(user){ + null, JsonNull -> null + is JsonObject -> user.jsonObject["name"]?.jsonPrimitive?.content + is JsonPrimitive -> user.content + else -> "@error" +} \ No newline at end of file diff --git a/magix/magix-storage/build.gradle.kts b/magix/magix-storage/build.gradle.kts index 43306c4..76bc2df 100644 --- a/magix/magix-storage/build.gradle.kts +++ b/magix/magix-storage/build.gradle.kts @@ -15,6 +15,7 @@ kscience { useSerialization { json() } + useCoroutines() dependencies { api(projects.magix.magixApi) api(spclibs.kotlinx.datetime) diff --git a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt index bfd7508..4a3efa3 100644 --- a/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt +++ b/magix/magix-storage/magix-storage-xodus/src/main/kotlin/space/kscience/magix/storage/xodus/XodusMagixStorage.kt @@ -6,12 +6,11 @@ import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.serialization.encodeToString import kotlinx.serialization.json.JsonObject -import kotlinx.serialization.json.JsonPrimitive -import kotlinx.serialization.json.jsonPrimitive import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixEndpoint.Companion.magixJson import space.kscience.magix.api.MagixMessage import space.kscience.magix.api.MagixMessageFilter +import space.kscience.magix.api.userName import space.kscience.magix.storage.* import java.nio.file.Path import kotlin.sequences.Sequence @@ -31,7 +30,7 @@ private fun Entity.parseMagixMessage(): MagixMessage = MagixMessage( }, ) -public class XodusMagixHistory(private val store: PersistentEntityStore) : MagixHistory { +public class XodusMagixHistory(private val store: PersistentEntityStore) : WriteableMagixHistory { public fun writeMessage(storeTransaction: StoreTransaction, message: MagixMessage) { storeTransaction.newEntity(XodusMagixStorage.MAGIC_MESSAGE_ENTITY_TYPE).apply { @@ -49,20 +48,13 @@ public class XodusMagixHistory(private val store: PersistentEntityStore) : Magix message.parentId?.let { setProperty(MagixMessage::parentId.name, it) } - message.user?.let { - setProperty( - MagixMessage::user.name, - when (it) { - is JsonObject -> it["name"]?.jsonPrimitive?.content ?: "@error" - is JsonPrimitive -> it.content - else -> "@error" - } - ) + message.userName?.let { + setProperty(MagixMessage::user.name, it) } } } - public fun sendMessage(message: MagixMessage) { + override suspend fun send(message: MagixMessage) { store.executeInTransaction { transaction -> writeMessage(transaction, message) } @@ -139,6 +131,7 @@ public class XodusMagixHistory(private val store: PersistentEntityStore) : Magix } } + /** * Attach a Xodus storage process to the given endpoint. */ @@ -154,7 +147,7 @@ public class XodusMagixStorage( //TODO consider message buffering private val subscriptionJob = endpoint.subscribe(subscriptionFilter).onEach { message -> - history.sendMessage(message) + history.send(message) }.launchIn(scope) private val broadcastJob = endpoint.launchHistory(scope, history, endpointName = endpointName) diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt index 3059481..5501dc5 100644 --- a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/MagixHistory.kt @@ -4,7 +4,9 @@ import kotlinx.datetime.LocalDateTime import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.doubleOrNull import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive import space.kscience.magix.api.MagixFormat import space.kscience.magix.api.MagixMessage import space.kscience.magix.api.MagixMessageFilter @@ -56,8 +58,17 @@ private fun JsonElement.takeElement(path: String): JsonElement? = if (path.isEmp public fun MagixPayloadFilter.test(element: JsonElement): Boolean = when (this) { is MagixPayloadFilter.Equals -> element.takeElement(path) == value - is MagixPayloadFilter.DateTimeInRange -> TODO() - is MagixPayloadFilter.NumberInRange -> TODO() + is MagixPayloadFilter.DateTimeInRange -> { + element.takeElement(path)?.jsonPrimitive?.content?.let { + LocalDateTime.parse(it) in from..to + } ?: false + } + is MagixPayloadFilter.NumberInRange -> { + element.takeElement(path)?.jsonPrimitive?.doubleOrNull?.let { + it in (from.toDouble()..to.toDouble()) + } ?: false + } + is MagixPayloadFilter.Not -> !argument.test(element) is MagixPayloadFilter.And -> left.test(element) && right.test(element) is MagixPayloadFilter.Or -> left.test(element) || right.test(element) @@ -103,3 +114,7 @@ public interface MagixHistory { } } +public interface WriteableMagixHistory: MagixHistory{ + public suspend fun send(message: MagixMessage) +} + diff --git a/magix/magix-storage/src/commonTest/kotlin/InMemoryMagixHistory.kt b/magix/magix-storage/src/commonTest/kotlin/InMemoryMagixHistory.kt new file mode 100644 index 0000000..432c6d8 --- /dev/null +++ b/magix/magix-storage/src/commonTest/kotlin/InMemoryMagixHistory.kt @@ -0,0 +1,32 @@ +package space.kscience.magix.storage + +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter + +class InMemoryMagixHistory() : WriteableMagixHistory { + + private val cache = mutableListOf() + private val mutex = Mutex() + + override suspend fun send(message: MagixMessage) { + mutex.withLock { + cache.add(message) + } + } + + override suspend fun useMessages( + magixFilter: MagixMessageFilter?, + payloadFilter: MagixPayloadFilter?, + userFilter: MagixUsernameFilter?, + callback: (Sequence) -> Unit, + ) = mutex.withLock { + val sequence = cache.asSequence().filter { message -> + (magixFilter?.accepts(message) ?: true) && + (userFilter?.userName?.equals(message.user) ?: true) && + payloadFilter?.test(message.payload) ?: true + } + callback(sequence) + } +} \ No newline at end of file From fa4f578a64dbfa58da999b4d768cc5902aecd872 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 3 Aug 2023 19:21:48 +0300 Subject: [PATCH 35/43] Update version --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 8034b20..b854502 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ val xodusVersion by extra("2.0.1") allprojects { group = "space.kscience" - version = "0.2.0-dev-1" + version = "0.2.0-dev-2" repositories{ maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") } From 69617e73b40bd7e959346fa9276c4e16f38ad79c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 16 Aug 2023 10:37:02 +0300 Subject: [PATCH 36/43] Replace JSSC with JSerialComm --- .../kscience/controls/ports/ChannelPort.kt | 4 +- controls-serial/build.gradle.kts | 2 +- .../controls/serial/JSerialCommPort.kt | 87 +++++++++++++++++ .../kscience/controls/serial/SerialPort.kt | 93 ------------------- .../controls/serial/SerialPortPlugin.kt | 2 +- magix/build.gradle.kts | 3 - .../space/kscience/magix/api/MagixEndpoint.kt | 8 +- .../{connections => services}/magixPortal.kt | 2 +- magix/rfc | 2 +- 9 files changed, 97 insertions(+), 106 deletions(-) create mode 100644 controls-serial/src/main/kotlin/space/kscience/controls/serial/JSerialCommPort.kt delete mode 100644 controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt rename magix/magix-api/src/commonMain/kotlin/space/kscience/magix/{connections => services}/magixPortal.kt (96%) diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt index d7983f2..23a57da 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt @@ -73,7 +73,7 @@ public class ChannelPort( } /** - * A [PortFactory] for TCP connections + * A [PortFactory] for TCP services */ public object TcpPort : PortFactory { @@ -97,7 +97,7 @@ public object TcpPort : PortFactory { /** - * A [PortFactory] for UDP connections + * A [PortFactory] for UDP services */ public object UdpPort : PortFactory { diff --git a/controls-serial/build.gradle.kts b/controls-serial/build.gradle.kts index b28a210..006a7c2 100644 --- a/controls-serial/build.gradle.kts +++ b/controls-serial/build.gradle.kts @@ -5,5 +5,5 @@ plugins { dependencies{ api(project(":controls-core")) - implementation("org.scream3r:jssc:2.8.0") + implementation("com.fazecast:jSerialComm:2.10.3") } \ No newline at end of file diff --git a/controls-serial/src/main/kotlin/space/kscience/controls/serial/JSerialCommPort.kt b/controls-serial/src/main/kotlin/space/kscience/controls/serial/JSerialCommPort.kt new file mode 100644 index 0000000..3e0601c --- /dev/null +++ b/controls-serial/src/main/kotlin/space/kscience/controls/serial/JSerialCommPort.kt @@ -0,0 +1,87 @@ +package space.kscience.controls.serial + +import com.fazecast.jSerialComm.SerialPort +import com.fazecast.jSerialComm.SerialPortDataListener +import com.fazecast.jSerialComm.SerialPortEvent +import kotlinx.coroutines.launch +import space.kscience.controls.ports.AbstractPort +import space.kscience.controls.ports.Port +import space.kscience.controls.ports.PortFactory +import space.kscience.dataforge.context.Context +import space.kscience.dataforge.meta.Meta +import space.kscience.dataforge.meta.int +import space.kscience.dataforge.meta.string +import kotlin.coroutines.CoroutineContext + +/** + * A port based on JSerialComm + */ +public class JSerialCommPort( + context: Context, + private val comPort: SerialPort, + coroutineContext: CoroutineContext = context.coroutineContext, +) : AbstractPort(context, coroutineContext) { + + override fun toString(): String = "port[${comPort.descriptivePortName}]" + + private val serialPortListener = object : SerialPortDataListener { + override fun getListeningEvents(): Int = SerialPort.LISTENING_EVENT_DATA_AVAILABLE + + override fun serialEvent(event: SerialPortEvent) { + if (event.eventType == SerialPort.LISTENING_EVENT_DATA_AVAILABLE) { + scope.launch { receive(event.receivedData) } + } + } + } + + init { + comPort.addDataListener(serialPortListener) + } + + override suspend fun write(data: ByteArray) { + comPort.writeBytes(data, data.size) + } + + override fun close() { + comPort.removeDataListener() + if (comPort.isOpen) { + comPort.closePort() + } + super.close() + } + + public companion object : PortFactory { + + override val type: String = "com" + + + /** + * Construct ComPort with given parameters + */ + public fun open( + context: Context, + portName: String, + baudRate: Int = 9600, + dataBits: Int = 8, + stopBits: Int = SerialPort.ONE_STOP_BIT, + parity: Int = SerialPort.NO_PARITY, + coroutineContext: CoroutineContext = context.coroutineContext, + ): JSerialCommPort { + val serialPort = SerialPort.getCommPort(portName).apply { + setComPortParameters(baudRate, dataBits, stopBits, parity) + openPort() + } + return JSerialCommPort(context, serialPort, coroutineContext) + } + + override fun build(context: Context, meta: Meta): Port { + val name by meta.string { error("Serial port name not defined") } + val baudRate by meta.int(9600) + val dataBits by meta.int(8) + val stopBits by meta.int(SerialPort.ONE_STOP_BIT) + val parity by meta.int(SerialPort.NO_PARITY) + return open(context, name, baudRate, dataBits, stopBits, parity) + } + } + +} \ No newline at end of file diff --git a/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt deleted file mode 100644 index 54b7ac8..0000000 --- a/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPort.kt +++ /dev/null @@ -1,93 +0,0 @@ -package space.kscience.controls.serial - -import jssc.SerialPort.* -import jssc.SerialPortEventListener -import kotlinx.coroutines.launch -import space.kscience.controls.ports.AbstractPort -import space.kscience.controls.ports.Port -import space.kscience.controls.ports.PortFactory -import space.kscience.dataforge.context.Context -import space.kscience.dataforge.meta.Meta -import space.kscience.dataforge.meta.int -import space.kscience.dataforge.meta.string -import kotlin.coroutines.CoroutineContext -import jssc.SerialPort as JSSCPort - -/** - * COM/USB port - */ -public class SerialPort private constructor( - context: Context, - private val jssc: JSSCPort, - coroutineContext: CoroutineContext = context.coroutineContext, -) : AbstractPort(context, coroutineContext) { - - override fun toString(): String = "port[${jssc.portName}]" - - private val serialPortListener = SerialPortEventListener { event -> - if (event.isRXCHAR) { - val chars = event.eventValue - val bytes = jssc.readBytes(chars) - scope.launch { receive(bytes) } - } - } - - init { - jssc.addEventListener(serialPortListener) - } - - /** - * Clear current input and output buffers - */ - internal fun clearPort() { - jssc.purgePort(PURGE_RXCLEAR or PURGE_TXCLEAR) - } - - override suspend fun write(data: ByteArray) { - jssc.writeBytes(data) - } - - @Throws(Exception::class) - override fun close() { - jssc.removeEventListener() - clearPort() - if (jssc.isOpened) { - jssc.closePort() - } - super.close() - } - - public companion object : PortFactory { - - override val type: String = "com" - - - /** - * Construct ComPort with given parameters - */ - public fun open( - context: Context, - portName: String, - baudRate: Int = BAUDRATE_9600, - dataBits: Int = DATABITS_8, - stopBits: Int = STOPBITS_1, - parity: Int = PARITY_NONE, - coroutineContext: CoroutineContext = context.coroutineContext, - ): SerialPort { - val jssc = JSSCPort(portName).apply { - openPort() - setParams(baudRate, dataBits, stopBits, parity) - } - return SerialPort(context, jssc, coroutineContext) - } - - override fun build(context: Context, meta: Meta): Port { - val name by meta.string { error("Serial port name not defined") } - val baudRate by meta.int(BAUDRATE_9600) - val dataBits by meta.int(DATABITS_8) - val stopBits by meta.int(STOPBITS_1) - val parity by meta.int(PARITY_NONE) - return open(context, name, baudRate, dataBits, stopBits, parity) - } - } -} \ No newline at end of file diff --git a/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPortPlugin.kt b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPortPlugin.kt index 6b573ae..ae9d4fc 100644 --- a/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPortPlugin.kt +++ b/controls-serial/src/main/kotlin/space/kscience/controls/serial/SerialPortPlugin.kt @@ -13,7 +13,7 @@ public class SerialPortPlugin : AbstractPlugin() { override val tag: PluginTag get() = Companion.tag override fun content(target: String): Map = when(target){ - PortFactory.TYPE -> mapOf(Name.EMPTY to SerialPort) + PortFactory.TYPE -> mapOf(Name.EMPTY to JSerialCommPort) else -> emptyMap() } diff --git a/magix/build.gradle.kts b/magix/build.gradle.kts index ae31ca2..e69de29 100644 --- a/magix/build.gradle.kts +++ b/magix/build.gradle.kts @@ -1,3 +0,0 @@ -subprojects{ - -} \ No newline at end of file diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt index 35cb8e5..7658cd9 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt @@ -28,22 +28,22 @@ public interface MagixEndpoint { public companion object { /** - * A default port for HTTP/WS connections + * A default port for HTTP/WS services */ public const val DEFAULT_MAGIX_HTTP_PORT: Int = 7777 /** - * A default port for raw TCP connections + * A default port for raw TCP services */ public const val DEFAULT_MAGIX_RAW_PORT: Int = 7778 /** - * A default PUB port for ZMQ connections + * A default PUB port for ZMQ services */ public const val DEFAULT_MAGIX_ZMQ_PUB_PORT: Int = 7781 /** - * A default PULL port for ZMQ connections + * A default PULL port for ZMQ services */ public const val DEFAULT_MAGIX_ZMQ_PULL_PORT: Int = 7782 diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/connections/magixPortal.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/magixPortal.kt similarity index 96% rename from magix/magix-api/src/commonMain/kotlin/space/kscience/magix/connections/magixPortal.kt rename to magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/magixPortal.kt index 9ede63f..d9ed009 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/connections/magixPortal.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/magixPortal.kt @@ -1,4 +1,4 @@ -package space.kscience.magix.connections +package space.kscience.magix.services import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job diff --git a/magix/rfc b/magix/rfc index 5ae42aa..7f50062 160000 --- a/magix/rfc +++ b/magix/rfc @@ -1 +1 @@ -Subproject commit 5ae42aa297fbd2ab2239601f064e1d1239487590 +Subproject commit 7f50062bd42dd5046110326cae2492ad1f7a195f From 583fe5b54c90e7f94faee58a43cc79bfea67bd50 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 16 Aug 2023 10:42:44 +0300 Subject: [PATCH 37/43] Replace JSSC with JSerialComm --- .../kotlin/space/kscience/controls/ports/ChannelPort.kt | 4 ++-- .../kotlin/space/kscience/magix/api/MagixEndpoint.kt | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt index 23a57da..d7983f2 100644 --- a/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt +++ b/controls-core/src/jvmMain/kotlin/space/kscience/controls/ports/ChannelPort.kt @@ -73,7 +73,7 @@ public class ChannelPort( } /** - * A [PortFactory] for TCP services + * A [PortFactory] for TCP connections */ public object TcpPort : PortFactory { @@ -97,7 +97,7 @@ public object TcpPort : PortFactory { /** - * A [PortFactory] for UDP services + * A [PortFactory] for UDP connections */ public object UdpPort : PortFactory { diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt index 7658cd9..35cb8e5 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt @@ -28,22 +28,22 @@ public interface MagixEndpoint { public companion object { /** - * A default port for HTTP/WS services + * A default port for HTTP/WS connections */ public const val DEFAULT_MAGIX_HTTP_PORT: Int = 7777 /** - * A default port for raw TCP services + * A default port for raw TCP connections */ public const val DEFAULT_MAGIX_RAW_PORT: Int = 7778 /** - * A default PUB port for ZMQ services + * A default PUB port for ZMQ connections */ public const val DEFAULT_MAGIX_ZMQ_PUB_PORT: Int = 7781 /** - * A default PULL port for ZMQ services + * A default PULL port for ZMQ connections */ public const val DEFAULT_MAGIX_ZMQ_PULL_PORT: Int = 7782 From ce03540b37105faf7ff6d133d105cf89bee182dc Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 16 Aug 2023 18:26:21 +0300 Subject: [PATCH 38/43] Add magix message registry service --- README.md | 15 ++-- controls-magix-client/README.md | 6 +- controls-modbus/README.md | 28 +++++++ controls-opcua/README.md | 28 +++++++ controls-ports-ktor/README.md | 30 ++++++- controls-serial/README.md | 6 +- controls-server/README.md | 6 +- controls-storage/README.md | 6 +- controls-storage/controls-xodus/README.md | 6 +- magix/magix-api/README.md | 6 +- .../space/kscience/magix/api/MagixEndpoint.kt | 7 +- .../space/kscience/magix/api/MagixRegistry.kt | 17 ---- .../space/kscience/magix/api/converters.kt | 30 ------- .../kscience/magix/services/MagixRegistry.kt | 81 +++++++++++++++++++ .../kscience/magix/services/converters.kt | 41 ++++++++++ magix/magix-java-client/README.md | 6 +- magix/magix-mqtt/README.md | 6 +- magix/magix-rabbit/README.md | 6 +- magix/magix-rsocket/README.md | 6 +- magix/magix-server/README.md | 6 +- magix/magix-storage/README.md | 28 +++++++ .../magix-storage-xodus/README.md | 6 +- magix/magix-zmq/README.md | 6 +- 23 files changed, 290 insertions(+), 93 deletions(-) delete mode 100644 magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixRegistry.kt delete mode 100644 magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/converters.kt create mode 100644 magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt create mode 100644 magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/converters.kt diff --git a/README.md b/README.md index 098569b..9535868 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,6 @@ Example view of a demo: > - [deviceHub](controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt) : Grouping of devices into local tree-like hubs. -### [controls-ktor-tcp](controls-ktor-tcp) -> -> -> **Maturity**: EXPERIMENTAL - ### [controls-magix-client](controls-magix-client) > > @@ -73,6 +68,16 @@ Example view of a demo: > > **Maturity**: EXPERIMENTAL +### [controls-pi](controls-pi) +> +> +> **Maturity**: EXPERIMENTAL + +### [controls-ports-ktor](controls-ports-ktor) +> +> +> **Maturity**: EXPERIMENTAL + ### [controls-serial](controls-serial) > > diff --git a/controls-magix-client/README.md b/controls-magix-client/README.md index 9e3187b..eec6dc1 100644 --- a/controls-magix-client/README.md +++ b/controls-magix-client/README.md @@ -6,7 +6,7 @@ Magix service for binding controls devices (both as RPC client and server ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-magix-client:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:controls-magix-client:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:controls-magix-client:0.1.1-SNAPSHOT' + implementation 'space.kscience:controls-magix-client:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:controls-magix-client:0.1.1-SNAPSHOT") + implementation("space.kscience:controls-magix-client:0.2.0-dev-2") } ``` diff --git a/controls-modbus/README.md b/controls-modbus/README.md index f6d2335..1e9a395 100644 --- a/controls-modbus/README.md +++ b/controls-modbus/README.md @@ -2,3 +2,31 @@ A plugin for Controls-kt device server on top of modbus-rtu/modbus-tcp protocols +## Usage + +## Artifact: + +The Maven coordinates of this project are `space.kscience:controls-modbus:0.2.0-dev-2`. + +**Gradle Groovy:** +```groovy +repositories { + maven { url 'https://repo.kotlin.link' } + mavenCentral() +} + +dependencies { + implementation 'space.kscience:controls-modbus:0.2.0-dev-2' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + mavenCentral() +} + +dependencies { + implementation("space.kscience:controls-modbus:0.2.0-dev-2") +} +``` diff --git a/controls-opcua/README.md b/controls-opcua/README.md index 3accf5b..a365cb2 100644 --- a/controls-opcua/README.md +++ b/controls-opcua/README.md @@ -2,3 +2,31 @@ +## Usage + +## Artifact: + +The Maven coordinates of this project are `space.kscience:controls-opcua:0.2.0-dev-2`. + +**Gradle Groovy:** +```groovy +repositories { + maven { url 'https://repo.kotlin.link' } + mavenCentral() +} + +dependencies { + implementation 'space.kscience:controls-opcua:0.2.0-dev-2' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + mavenCentral() +} + +dependencies { + implementation("space.kscience:controls-opcua:0.2.0-dev-2") +} +``` diff --git a/controls-ports-ktor/README.md b/controls-ports-ktor/README.md index 94deb3b..b43e48c 100644 --- a/controls-ports-ktor/README.md +++ b/controls-ports-ktor/README.md @@ -1,4 +1,32 @@ -# Module controls-ktor-tcp +# Module controls-ports-ktor +## Usage + +## Artifact: + +The Maven coordinates of this project are `space.kscience:controls-ports-ktor:0.2.0-dev-2`. + +**Gradle Groovy:** +```groovy +repositories { + maven { url 'https://repo.kotlin.link' } + mavenCentral() +} + +dependencies { + implementation 'space.kscience:controls-ports-ktor:0.2.0-dev-2' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + mavenCentral() +} + +dependencies { + implementation("space.kscience:controls-ports-ktor:0.2.0-dev-2") +} +``` diff --git a/controls-serial/README.md b/controls-serial/README.md index eb214ba..bb2405a 100644 --- a/controls-serial/README.md +++ b/controls-serial/README.md @@ -6,7 +6,7 @@ ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-serial:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:controls-serial:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:controls-serial:0.1.1-SNAPSHOT' + implementation 'space.kscience:controls-serial:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:controls-serial:0.1.1-SNAPSHOT") + implementation("space.kscience:controls-serial:0.2.0-dev-2") } ``` diff --git a/controls-server/README.md b/controls-server/README.md index 561a815..e2218a0 100644 --- a/controls-server/README.md +++ b/controls-server/README.md @@ -6,7 +6,7 @@ A magix event loop server with web server for visualization. ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-server:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:controls-server:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:controls-server:0.1.1-SNAPSHOT' + implementation 'space.kscience:controls-server:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:controls-server:0.1.1-SNAPSHOT") + implementation("space.kscience:controls-server:0.2.0-dev-2") } ``` diff --git a/controls-storage/README.md b/controls-storage/README.md index 650dd53..a70b2e2 100644 --- a/controls-storage/README.md +++ b/controls-storage/README.md @@ -6,7 +6,7 @@ ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-storage:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:controls-storage:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:controls-storage:0.1.1-SNAPSHOT' + implementation 'space.kscience:controls-storage:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:controls-storage:0.1.1-SNAPSHOT") + implementation("space.kscience:controls-storage:0.2.0-dev-2") } ``` diff --git a/controls-storage/controls-xodus/README.md b/controls-storage/controls-xodus/README.md index a25650e..cb7b761 100644 --- a/controls-storage/controls-xodus/README.md +++ b/controls-storage/controls-xodus/README.md @@ -6,7 +6,7 @@ ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-xodus:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:controls-xodus:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:controls-xodus:0.1.1-SNAPSHOT' + implementation 'space.kscience:controls-xodus:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:controls-xodus:0.1.1-SNAPSHOT") + implementation("space.kscience:controls-xodus:0.2.0-dev-2") } ``` diff --git a/magix/magix-api/README.md b/magix/magix-api/README.md index 0226fc6..e1207ab 100644 --- a/magix/magix-api/README.md +++ b/magix/magix-api/README.md @@ -6,7 +6,7 @@ ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-api:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:magix-api:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:magix-api:0.1.1-SNAPSHOT' + implementation 'space.kscience:magix-api:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:magix-api:0.1.1-SNAPSHOT") + implementation("space.kscience:magix-api:0.2.0-dev-2") } ``` diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt index 35cb8e5..e1ccc23 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt @@ -53,4 +53,9 @@ public interface MagixEndpoint { encodeDefaults = false } } -} \ No newline at end of file +} + +/** + * An alias for [MagixEndpoint.broadcast] + */ +public suspend fun MagixEndpoint.send(message: MagixMessage): Unit = broadcast(message) \ No newline at end of file diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixRegistry.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixRegistry.kt deleted file mode 100644 index 7b3b111..0000000 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixRegistry.kt +++ /dev/null @@ -1,17 +0,0 @@ -package space.kscience.magix.api - -import kotlinx.serialization.json.JsonElement - -/** - * An interface to access distributed Magix property registry - */ -public interface MagixRegistry { - /** - * Request a property with name [propertyName] and user authentication data [user]. - * - * Return a property value in its generic form or null if it is not present. - * - * Throw an exception if property is present but access is denied. - */ - public suspend fun request(propertyName: String, user: JsonElement? = null): JsonElement? -} diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/converters.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/converters.kt deleted file mode 100644 index beed930..0000000 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/converters.kt +++ /dev/null @@ -1,30 +0,0 @@ -package space.kscience.magix.api - -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.serialization.json.JsonElement - -/** - * Launch magix message converter service - */ -public fun CoroutineScope.launchMagixConverter( - endpoint: MagixEndpoint, - filter: MagixMessageFilter, - outputFormat: String, - newOrigin: String? = null, - transformer: suspend (JsonElement) -> JsonElement, -): Job = endpoint.subscribe(filter).onEach { message-> - val newPayload = transformer(message.payload) - val transformed: MagixMessage = MagixMessage( - outputFormat, - newPayload, - newOrigin ?: message.sourceEndpoint, - message.targetEndpoint, - message.id, - message.parentId, - message.user - ) - endpoint.broadcast(transformed) -}.launchIn(this) diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt new file mode 100644 index 0000000..993d246 --- /dev/null +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt @@ -0,0 +1,81 @@ +package space.kscience.magix.services + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixFormat +import space.kscience.magix.api.broadcast +import space.kscience.magix.api.subscribe + +/** + * An interface to access distributed Magix property registry + */ +public interface MagixRegistry { + /** + * Request a property with name [propertyName] and user authentication data [user]. + * + * Return a property value in its generic form or null if it is not present. + * + * Throw exception access is denied, or request failed. + */ + public suspend fun request(propertyName: String, user: JsonElement? = null): JsonElement? +} + +@Serializable +public sealed class MagixRegistryMessage { + public abstract val propertyName: String + + public companion object { + public val format: MagixFormat = MagixFormat(serializer(), setOf("magix.registry")) + } +} + +@Serializable +@SerialName("registry.request") +public class MagixRegistryRequestMessage( + override val propertyName: String, +) : MagixRegistryMessage() + +@Serializable +@SerialName("registry.value") +public class MagixRegistryValueMessage( + override val propertyName: String, + public val value: JsonElement, +) : MagixRegistryMessage() + +@Serializable +@SerialName("registry.error") +public class MagixRegistryErrorMessage( + override val propertyName: String, + public val errorType: String?, + public val errorMessage: String? = null, +) : MagixRegistryMessage() + +public fun CoroutineScope.launchMagixRegistry( + endpoint: MagixEndpoint, + registry: MagixRegistry, + originFilter: Collection? = null, + targetFilter: Collection? = null, +): Job = endpoint.subscribe(MagixRegistryMessage.format, originFilter, targetFilter) + .onEach { (magixMessage, payload) -> + if (payload is MagixRegistryRequestMessage) { + try { + val value = registry.request(payload.propertyName, magixMessage.user) + endpoint.broadcast( + MagixRegistryMessage.format, + MagixRegistryValueMessage(payload.propertyName, value ?: JsonNull) + ) + } catch (ex: Exception){ + endpoint.broadcast( + MagixRegistryMessage.format, + MagixRegistryErrorMessage(payload.propertyName, ex::class.simpleName, ex.message) + ) + } + } + }.launchIn(this) \ No newline at end of file diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/converters.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/converters.kt new file mode 100644 index 0000000..ac6e6e2 --- /dev/null +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/converters.kt @@ -0,0 +1,41 @@ +package space.kscience.magix.services + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.serialization.json.JsonElement +import space.kscience.magix.api.MagixEndpoint +import space.kscience.magix.api.MagixMessage +import space.kscience.magix.api.MagixMessageFilter + +/** + * Launch magix message converter service. + * + * The service converts a payload from one format into another. + * + * @param endpoint The endpoint this converter is attached to + * @param filter a filter for messages to be converted. + * @param outputFormat a new value for [MagixMessage.format] field + * @param newSource a new value of [MagixMessage.sourceEndpoint]. By default uses the original message value + * @param transformer a function to transform the payload. + */ +public fun CoroutineScope.launchMagixConverter( + endpoint: MagixEndpoint, + filter: MagixMessageFilter, + outputFormat: String, + newSource: String? = null, + transformer: suspend (JsonElement) -> JsonElement, +): Job = endpoint.subscribe(filter).onEach { message-> + val newPayload = transformer(message.payload) + val transformed: MagixMessage = MagixMessage( + outputFormat, + newPayload, + newSource ?: message.sourceEndpoint, + message.targetEndpoint, + message.id, + message.parentId, + message.user + ) + endpoint.broadcast(transformed) +}.launchIn(this) diff --git a/magix/magix-java-client/README.md b/magix/magix-java-client/README.md index 375d6df..a81787e 100644 --- a/magix/magix-java-client/README.md +++ b/magix/magix-java-client/README.md @@ -6,7 +6,7 @@ ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-java-client:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:magix-java-client:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:magix-java-client:0.1.1-SNAPSHOT' + implementation 'space.kscience:magix-java-client:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:magix-java-client:0.1.1-SNAPSHOT") + implementation("space.kscience:magix-java-client:0.2.0-dev-2") } ``` diff --git a/magix/magix-mqtt/README.md b/magix/magix-mqtt/README.md index d8b9da6..061803e 100644 --- a/magix/magix-mqtt/README.md +++ b/magix/magix-mqtt/README.md @@ -6,7 +6,7 @@ MQTT client magix endpoint ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-mqtt:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:magix-mqtt:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:magix-mqtt:0.1.1-SNAPSHOT' + implementation 'space.kscience:magix-mqtt:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:magix-mqtt:0.1.1-SNAPSHOT") + implementation("space.kscience:magix-mqtt:0.2.0-dev-2") } ``` diff --git a/magix/magix-rabbit/README.md b/magix/magix-rabbit/README.md index 7c60864..79b0af1 100644 --- a/magix/magix-rabbit/README.md +++ b/magix/magix-rabbit/README.md @@ -6,7 +6,7 @@ RabbitMQ client magix endpoint ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-rabbit:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:magix-rabbit:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:magix-rabbit:0.1.1-SNAPSHOT' + implementation 'space.kscience:magix-rabbit:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:magix-rabbit:0.1.1-SNAPSHOT") + implementation("space.kscience:magix-rabbit:0.2.0-dev-2") } ``` diff --git a/magix/magix-rsocket/README.md b/magix/magix-rsocket/README.md index 0d5c9e6..1307fbe 100644 --- a/magix/magix-rsocket/README.md +++ b/magix/magix-rsocket/README.md @@ -6,7 +6,7 @@ Magix endpoint (client) based on RSocket ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-rsocket:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:magix-rsocket:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:magix-rsocket:0.1.1-SNAPSHOT' + implementation 'space.kscience:magix-rsocket:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:magix-rsocket:0.1.1-SNAPSHOT") + implementation("space.kscience:magix-rsocket:0.2.0-dev-2") } ``` diff --git a/magix/magix-server/README.md b/magix/magix-server/README.md index f5a83e3..0ce680b 100644 --- a/magix/magix-server/README.md +++ b/magix/magix-server/README.md @@ -6,7 +6,7 @@ A magix event loop implementation in Kotlin. Includes HTTP/SSE and RSocket route ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-server:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:magix-server:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:magix-server:0.1.1-SNAPSHOT' + implementation 'space.kscience:magix-server:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:magix-server:0.1.1-SNAPSHOT") + implementation("space.kscience:magix-server:0.2.0-dev-2") } ``` diff --git a/magix/magix-storage/README.md b/magix/magix-storage/README.md index 7ebafae..fd9822f 100644 --- a/magix/magix-storage/README.md +++ b/magix/magix-storage/README.md @@ -1,4 +1,32 @@ # Module magix-storage +Magix history database API +## Usage +## Artifact: + +The Maven coordinates of this project are `space.kscience:magix-storage:0.2.0-dev-2`. + +**Gradle Groovy:** +```groovy +repositories { + maven { url 'https://repo.kotlin.link' } + mavenCentral() +} + +dependencies { + implementation 'space.kscience:magix-storage:0.2.0-dev-2' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + mavenCentral() +} + +dependencies { + implementation("space.kscience:magix-storage:0.2.0-dev-2") +} +``` diff --git a/magix/magix-storage/magix-storage-xodus/README.md b/magix/magix-storage/magix-storage-xodus/README.md index 57107c5..bcc8a6e 100644 --- a/magix/magix-storage/magix-storage-xodus/README.md +++ b/magix/magix-storage/magix-storage-xodus/README.md @@ -6,7 +6,7 @@ ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-storage-xodus:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:magix-storage-xodus:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:magix-storage-xodus:0.1.1-SNAPSHOT' + implementation 'space.kscience:magix-storage-xodus:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:magix-storage-xodus:0.1.1-SNAPSHOT") + implementation("space.kscience:magix-storage-xodus:0.2.0-dev-2") } ``` diff --git a/magix/magix-zmq/README.md b/magix/magix-zmq/README.md index cf150fe..65c4892 100644 --- a/magix/magix-zmq/README.md +++ b/magix/magix-zmq/README.md @@ -6,7 +6,7 @@ ZMQ client endpoint for Magix ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-zmq:0.1.1-SNAPSHOT`. +The Maven coordinates of this project are `space.kscience:magix-zmq:0.2.0-dev-2`. **Gradle Groovy:** ```groovy @@ -16,7 +16,7 @@ repositories { } dependencies { - implementation 'space.kscience:magix-zmq:0.1.1-SNAPSHOT' + implementation 'space.kscience:magix-zmq:0.2.0-dev-2' } ``` **Gradle Kotlin DSL:** @@ -27,6 +27,6 @@ repositories { } dependencies { - implementation("space.kscience:magix-zmq:0.1.1-SNAPSHOT") + implementation("space.kscience:magix-zmq:0.2.0-dev-2") } ``` From 82dbb3a1f0400be77ba1cef5c4bf83d6fe6331d4 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 16 Aug 2023 20:27:23 +0300 Subject: [PATCH 39/43] broadcast -> send --- .../kotlin/space/kscience/controls/client/DeviceClient.kt | 4 ++-- .../kotlin/space/kscience/controls/client/controlsMagix.kt | 4 ++-- .../kotlin/space/kscience/controls/client/tangoMagix.kt | 4 ++-- .../kotlin/space/kscience/magix/api/MagixEndpoint.kt | 2 +- .../kotlin/space/kscience/magix/api/MagixFormat.kt | 2 +- .../kotlin/space/kscience/magix/services/MagixRegistry.kt | 6 +++--- .../kotlin/space/kscience/magix/storage/historyEndpoint.kt | 4 ++-- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt index a724fd6..ce5dd75 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt @@ -12,7 +12,7 @@ import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.misc.DFExperimental import space.kscience.dataforge.names.Name import space.kscience.magix.api.MagixEndpoint -import space.kscience.magix.api.broadcast +import space.kscience.magix.api.send import space.kscience.magix.api.subscribe import kotlin.coroutines.CoroutineContext @@ -108,6 +108,6 @@ public class DeviceClient( public fun MagixEndpoint.remoteDevice(context: Context, magixTarget: String, deviceName: Name): DeviceClient { val subscription = subscribe(DeviceManager.magixFormat, originFilter = listOf(magixTarget)).map { it.second } return DeviceClient(context, deviceName, subscription) { - broadcast(DeviceManager.magixFormat, it, magixTarget, id = stringUID()) + send(DeviceManager.magixFormat, it, magixTarget, id = stringUID()) } } \ No newline at end of file diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt index c129c79..e986fc5 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt @@ -40,7 +40,7 @@ public fun DeviceManager.connectToMagix( endpoint.subscribe(controlsMagixFormat, targetFilter = listOf(endpointID)).onEach { (request, payload) -> val responsePayload = respondHubMessage(payload) if (responsePayload != null) { - endpoint.broadcast( + endpoint.send( format = controlsMagixFormat, target = request.sourceEndpoint, origin = endpointID, @@ -54,7 +54,7 @@ public fun DeviceManager.connectToMagix( }.launchIn(this) hubMessageFlow(this).onEach { payload -> - endpoint.broadcast( + endpoint.send( format = controlsMagixFormat, origin = endpointID, payload = payload, diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt index 48ae3c5..e7ee969 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt +++ b/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt @@ -75,7 +75,7 @@ public fun DeviceManager.launchTangoMagix( ): Job { suspend fun respond(request: MagixMessage, payload: TangoPayload, payloadBuilder: (TangoPayload) -> TangoPayload) { - endpoint.broadcast( + endpoint.send( tangoMagixFormat, id = generateId(request), parentId = request.id, @@ -125,7 +125,7 @@ public fun DeviceManager.launchTangoMagix( } } catch (ex: Exception) { logger.error(ex) { "Error while responding to message" } - endpoint.broadcast( + endpoint.send( tangoMagixFormat, id = generateId(request), parentId = request.id, diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt index e1ccc23..19cf2f5 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixEndpoint.kt @@ -56,6 +56,6 @@ public interface MagixEndpoint { } /** - * An alias for [MagixEndpoint.broadcast] + * An alias for [MagixEndpoint.send] */ public suspend fun MagixEndpoint.send(message: MagixMessage): Unit = broadcast(message) \ No newline at end of file diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt index d1a02e8..39ef071 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt @@ -38,7 +38,7 @@ public fun MagixEndpoint.subscribe( * Send a message using given [format] to encode the message payload. The format field is also taken from [format]. * */ -public suspend fun MagixEndpoint.broadcast( +public suspend fun MagixEndpoint.send( format: MagixFormat, payload: T, target: String? = null, diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt index 993d246..e82dc24 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt @@ -10,7 +10,7 @@ import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonNull import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixFormat -import space.kscience.magix.api.broadcast +import space.kscience.magix.api.send import space.kscience.magix.api.subscribe /** @@ -67,12 +67,12 @@ public fun CoroutineScope.launchMagixRegistry( if (payload is MagixRegistryRequestMessage) { try { val value = registry.request(payload.propertyName, magixMessage.user) - endpoint.broadcast( + endpoint.send( MagixRegistryMessage.format, MagixRegistryValueMessage(payload.propertyName, value ?: JsonNull) ) } catch (ex: Exception){ - endpoint.broadcast( + endpoint.send( MagixRegistryMessage.format, MagixRegistryErrorMessage(payload.propertyName, ex::class.simpleName, ex.message) ) diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt index 67b2cd3..c0ec3f9 100644 --- a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt @@ -9,7 +9,7 @@ import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixMessage -import space.kscience.magix.api.broadcast +import space.kscience.magix.api.send import space.kscience.magix.api.subscribe internal fun generateId(request: MagixMessage): String = if (request.id != null) { @@ -47,7 +47,7 @@ public fun MagixEndpoint.launchHistory( pageNumber, lastPage = end ) - broadcast( + send( format = MagixHistory.magixFormat, payload = sendPayload, target = request.sourceEndpoint, From a9f29f92ca9498665b5901dcf3cf1cc7bfb48f3e Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 18 Aug 2023 13:01:46 +0300 Subject: [PATCH 40/43] Add magix registry access service --- controls-core/README.md | 39 +++++++++++++++++++ controls-pi/README.md | 32 +++++++++++++++ .../kscience/magix/services/MagixRegistry.kt | 34 +++++++++++++++- 3 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 controls-core/README.md create mode 100644 controls-pi/README.md diff --git a/controls-core/README.md b/controls-core/README.md new file mode 100644 index 0000000..1a8fa70 --- /dev/null +++ b/controls-core/README.md @@ -0,0 +1,39 @@ +# Module controls-core + + + +## Features + + - [device](src/commonMain/kotlin/space/kscience/controls/api/Device.kt) : Device API with subscription (asynchronous and pseudo-synchronous properties) + - [deviceMessage](src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt) : Specification for messages used to communicate between Controls-kt devices. + - [deviceHub](src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt) : Grouping of devices into local tree-like hubs. + + +## Usage + +## Artifact: + +The Maven coordinates of this project are `space.kscience:controls-core:0.2.0-dev-2`. + +**Gradle Groovy:** +```groovy +repositories { + maven { url 'https://repo.kotlin.link' } + mavenCentral() +} + +dependencies { + implementation 'space.kscience:controls-core:0.2.0-dev-2' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + mavenCentral() +} + +dependencies { + implementation("space.kscience:controls-core:0.2.0-dev-2") +} +``` diff --git a/controls-pi/README.md b/controls-pi/README.md new file mode 100644 index 0000000..9d91bcf --- /dev/null +++ b/controls-pi/README.md @@ -0,0 +1,32 @@ +# Module controls-pi + + + +## Usage + +## Artifact: + +The Maven coordinates of this project are `space.kscience:controls-pi:0.2.0-dev-2`. + +**Gradle Groovy:** +```groovy +repositories { + maven { url 'https://repo.kotlin.link' } + mavenCentral() +} + +dependencies { + implementation 'space.kscience:controls-pi:0.2.0-dev-2' +} +``` +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + mavenCentral() +} + +dependencies { + implementation("space.kscience:controls-pi:0.2.0-dev-2") +} +``` diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt index e82dc24..f6af81c 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt @@ -2,7 +2,9 @@ package space.kscience.magix.services import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapNotNull import kotlinx.coroutines.flow.onEach import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -57,6 +59,9 @@ public class MagixRegistryErrorMessage( public val errorMessage: String? = null, ) : MagixRegistryMessage() +/** + * Launch a magix registry loop service based on local registry + */ public fun CoroutineScope.launchMagixRegistry( endpoint: MagixEndpoint, registry: MagixRegistry, @@ -71,11 +76,36 @@ public fun CoroutineScope.launchMagixRegistry( MagixRegistryMessage.format, MagixRegistryValueMessage(payload.propertyName, value ?: JsonNull) ) - } catch (ex: Exception){ + } catch (ex: Exception) { endpoint.send( MagixRegistryMessage.format, MagixRegistryErrorMessage(payload.propertyName, ex::class.simpleName, ex.message) ) } } - }.launchIn(this) \ No newline at end of file + }.launchIn(this) + +/** + * Request a property with given name and return a [Flow] of pairs (sourceEndpoint, value). + * + * Flow is ordered by response receive time. + * The subscriber can terminate the flow at any moment to stop subscription, or use it indefinitely to continue observing changes. + * To request a single value, use [Flow.first] function. + * + * If [targetEndpoint] field is provided, send request only to given endpoint. + */ +public suspend fun MagixEndpoint.getProperty( + propertyName: String, + user: JsonElement? = null, + targetEndpoint: String? = null, +): Flow> { + send(MagixRegistryMessage.format, MagixRegistryRequestMessage(propertyName), target = targetEndpoint, user = user) + return subscribe( + MagixRegistryMessage.format, + originFilter = targetEndpoint?.let { setOf(it) } + ).mapNotNull { (message, response) -> + if (response is MagixRegistryValueMessage && response.propertyName == propertyName) { + message.sourceEndpoint to response.value + } else null + } +} \ No newline at end of file From 641daec7e90471db0a2897eb437570309c433f81 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Fri, 18 Aug 2023 20:17:23 +0300 Subject: [PATCH 41/43] ControlsMagix refactor --- .../README.md | 0 .../build.gradle.kts | 4 +- .../kscience/controls/client/DeviceClient.kt | 14 ++- .../kscience/controls/client/controlsMagix.kt | 8 +- .../kscience/controls/client/doocsMagix.kt | 0 .../kscience/controls/client/tangoMagix.kt | 12 +-- controls-storage/build.gradle.kts | 2 +- demo/all-things/build.gradle.kts | 2 +- .../controls/demo/DemoControllerView.kt | 4 +- demo/car/build.gradle.kts | 2 +- .../controls/demo/car/VirtualCarController.kt | 4 +- demo/many-devices/build.gradle.kts | 2 +- .../kscience/controls/demo/MassDevice.kt | 4 +- demo/motors/build.gradle.kts | 2 +- .../space/kscience/magix/api/MagixFormat.kt | 4 +- .../kscience/magix/services/MagixRegistry.kt | 94 ++++++++++++++----- .../kscience/magix/storage/historyEndpoint.kt | 2 +- settings.gradle.kts | 2 +- 18 files changed, 104 insertions(+), 58 deletions(-) rename {controls-magix-client => controls-magix}/README.md (100%) rename {controls-magix-client => controls-magix}/build.gradle.kts (86%) rename {controls-magix-client => controls-magix}/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt (86%) rename {controls-magix-client => controls-magix}/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt (95%) rename {controls-magix-client => controls-magix}/src/commonMain/kotlin/space/kscience/controls/client/doocsMagix.kt (100%) rename {controls-magix-client => controls-magix}/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt (95%) diff --git a/controls-magix-client/README.md b/controls-magix/README.md similarity index 100% rename from controls-magix-client/README.md rename to controls-magix/README.md diff --git a/controls-magix-client/build.gradle.kts b/controls-magix/build.gradle.kts similarity index 86% rename from controls-magix-client/build.gradle.kts rename to controls-magix/build.gradle.kts index cbdefcc..76c7956 100644 --- a/controls-magix-client/build.gradle.kts +++ b/controls-magix/build.gradle.kts @@ -4,7 +4,7 @@ plugins { } description = """ - Magix service for binding controls devices (both as RPC client and server + Magix service for binding controls devices (both as RPC client and server) """.trimIndent() kscience { @@ -16,7 +16,7 @@ kscience { dependencies { api(projects.magix.magixApi) api(projects.controlsCore) - api("com.benasher44:uuid:0.7.0") + api("com.benasher44:uuid:0.8.0") } } diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt b/controls-magix/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt similarity index 86% rename from controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt rename to controls-magix/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt index ce5dd75..64e8a9e 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt +++ b/controls-magix/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt @@ -19,7 +19,7 @@ import kotlin.coroutines.CoroutineContext private fun stringUID() = uuid4().leastSignificantBits.toString(16) /** - * An implementation of device via RPC + * A remote accessible device that relies on connection via Magix */ public class DeviceClient( override val context: Context, @@ -103,11 +103,15 @@ public class DeviceClient( } /** - * Connect to a remote device via this client. + * Connect to a remote device via this endpoint. + * + * @param context a [Context] to run device in + * @param endpointName the name of endpoint in Magix to connect to + * @param deviceName the name of device within endpoint */ -public fun MagixEndpoint.remoteDevice(context: Context, magixTarget: String, deviceName: Name): DeviceClient { - val subscription = subscribe(DeviceManager.magixFormat, originFilter = listOf(magixTarget)).map { it.second } +public fun MagixEndpoint.remoteDevice(context: Context, endpointName: String, deviceName: Name): DeviceClient { + val subscription = subscribe(DeviceManager.magixFormat, originFilter = listOf(endpointName)).map { it.second } return DeviceClient(context, deviceName, subscription) { - send(DeviceManager.magixFormat, it, magixTarget, id = stringUID()) + send(DeviceManager.magixFormat, it, endpointName, id = stringUID()) } } \ No newline at end of file diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt b/controls-magix/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt similarity index 95% rename from controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt rename to controls-magix/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt index e986fc5..ed69bff 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt +++ b/controls-magix/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt @@ -33,7 +33,7 @@ internal fun generateId(request: MagixMessage): String = if (request.id != null) /** * Communicate with server in [Magix format](https://github.com/waltz-controls/rfc/tree/master/1) */ -public fun DeviceManager.connectToMagix( +public fun DeviceManager.launchMagixService( endpoint: MagixEndpoint, endpointID: String = controlsMagixFormat.defaultFormat, ): Job = context.launch { @@ -42,9 +42,9 @@ public fun DeviceManager.connectToMagix( if (responsePayload != null) { endpoint.send( format = controlsMagixFormat, - target = request.sourceEndpoint, - origin = endpointID, payload = responsePayload, + source = endpointID, + target = request.sourceEndpoint, id = generateId(request), parentId = request.id ) @@ -56,8 +56,8 @@ public fun DeviceManager.connectToMagix( hubMessageFlow(this).onEach { payload -> endpoint.send( format = controlsMagixFormat, - origin = endpointID, payload = payload, + source = endpointID, id = "df[${payload.hashCode()}]" ) }.catch { error -> diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/doocsMagix.kt b/controls-magix/src/commonMain/kotlin/space/kscience/controls/client/doocsMagix.kt similarity index 100% rename from controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/doocsMagix.kt rename to controls-magix/src/commonMain/kotlin/space/kscience/controls/client/doocsMagix.kt diff --git a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt b/controls-magix/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt similarity index 95% rename from controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt rename to controls-magix/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt index e7ee969..d0bdda4 100644 --- a/controls-magix-client/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt +++ b/controls-magix/src/commonMain/kotlin/space/kscience/controls/client/tangoMagix.kt @@ -77,10 +77,10 @@ public fun DeviceManager.launchTangoMagix( suspend fun respond(request: MagixMessage, payload: TangoPayload, payloadBuilder: (TangoPayload) -> TangoPayload) { endpoint.send( tangoMagixFormat, + payload = payloadBuilder(payload), + source = endpointID, id = generateId(request), - parentId = request.id, - origin = endpointID, - payload = payloadBuilder(payload) + parentId = request.id ) } @@ -127,10 +127,10 @@ public fun DeviceManager.launchTangoMagix( logger.error(ex) { "Error while responding to message" } endpoint.send( tangoMagixFormat, + payload = payload.copy(quality = TangoQuality.WARNING), + source = endpointID, id = generateId(request), - parentId = request.id, - origin = endpointID, - payload = payload.copy(quality = TangoQuality.WARNING) + parentId = request.id ) } }.launchIn(this) diff --git a/controls-storage/build.gradle.kts b/controls-storage/build.gradle.kts index 6bf1aa2..a8c6c29 100644 --- a/controls-storage/build.gradle.kts +++ b/controls-storage/build.gradle.kts @@ -13,7 +13,7 @@ kscience{ } dependencies(jvmMain){ api(projects.magix.magixApi) - api(projects.controlsMagixClient) + api(projects.controlsMagix) api(projects.magix.magixServer) } } diff --git a/demo/all-things/build.gradle.kts b/demo/all-things/build.gradle.kts index 1ea3c05..05d7395 100644 --- a/demo/all-things/build.gradle.kts +++ b/demo/all-things/build.gradle.kts @@ -17,7 +17,7 @@ dependencies { implementation(projects.controlsCore) //implementation(projects.controlsServer) implementation(projects.magix.magixServer) - implementation(projects.controlsMagixClient) + implementation(projects.controlsMagix) implementation(projects.magix.magixRsocket) implementation(projects.magix.magixZmq) implementation(projects.controlsOpcua) 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 edb199e..94815fa 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 @@ -8,7 +8,7 @@ import javafx.stage.Stage import kotlinx.coroutines.launch import org.eclipse.milo.opcua.sdk.server.OpcUaServer import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText -import space.kscience.controls.client.connectToMagix +import space.kscience.controls.client.launchMagixService import space.kscience.controls.demo.DemoDevice.Companion.cosScale import space.kscience.controls.demo.DemoDevice.Companion.sinScale import space.kscience.controls.demo.DemoDevice.Companion.timeScale @@ -59,7 +59,7 @@ class DemoController : Controller(), ContextAware { ) //Launch a device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost") - deviceManager.connectToMagix(deviceEndpoint) + deviceManager.launchMagixService(deviceEndpoint) //connect visualization to a magix endpoint val visualEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost") visualizer = startDemoDeviceServer(visualEndpoint) diff --git a/demo/car/build.gradle.kts b/demo/car/build.gradle.kts index 3c943dd..5d53f11 100644 --- a/demo/car/build.gradle.kts +++ b/demo/car/build.gradle.kts @@ -19,7 +19,7 @@ dependencies { implementation(projects.magix.magixServer) implementation(projects.magix.magixRsocket) implementation(projects.magix.magixZmq) - implementation(projects.controlsMagixClient) + implementation(projects.controlsMagix) implementation(projects.controlsStorage.controlsXodus) implementation(projects.magix.magixStorage.magixStorageXodus) // implementation(projects.controlsMongo) 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 f68c585..7a170ac 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 @@ -8,7 +8,7 @@ import javafx.scene.layout.Priority import javafx.stage.Stage import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import space.kscience.controls.client.connectToMagix +import space.kscience.controls.client.launchMagixService import space.kscience.controls.demo.car.IVirtualCar.Companion.acceleration import space.kscience.controls.manager.DeviceManager import space.kscience.controls.manager.install @@ -63,7 +63,7 @@ class VirtualCarController : Controller(), ContextAware { //mongoStorageJob = deviceManager.storeMessages(DefaultAsynchronousMongoClientFactory) //Launch device client and connect it to the server val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost") - deviceManager.connectToMagix(deviceEndpoint) + deviceManager.launchMagixService(deviceEndpoint) } } diff --git a/demo/many-devices/build.gradle.kts b/demo/many-devices/build.gradle.kts index 277d47b..7248e42 100644 --- a/demo/many-devices/build.gradle.kts +++ b/demo/many-devices/build.gradle.kts @@ -14,7 +14,7 @@ val rsocketVersion: String by rootProject.extra dependencies { implementation(projects.magix.magixServer) - implementation(projects.controlsMagixClient) + implementation(projects.controlsMagix) implementation(projects.magix.magixRsocket) implementation(projects.magix.magixZmq) diff --git a/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt b/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt index 56b66b9..94ef9db 100644 --- a/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt +++ b/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.datetime.Clock -import space.kscience.controls.client.connectToMagix +import space.kscience.controls.client.launchMagixService import space.kscience.controls.client.magixFormat import space.kscience.controls.manager.DeviceManager import space.kscience.controls.manager.install @@ -78,7 +78,7 @@ suspend fun main() { val endpointId = "device$it" val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost") - deviceManager.connectToMagix(deviceEndpoint, endpointId) + deviceManager.launchMagixService(deviceEndpoint, endpointId) } } diff --git a/demo/motors/build.gradle.kts b/demo/motors/build.gradle.kts index 0acea4a..8626c72 100644 --- a/demo/motors/build.gradle.kts +++ b/demo/motors/build.gradle.kts @@ -24,6 +24,6 @@ val dataforgeVersion: String by extra dependencies { implementation(project(":controls-ports-ktor")) - implementation(project(":controls-magix-client")) + implementation(projects.controlsMagix) implementation("no.tornado:tornadofx:1.7.20") } diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt index 39ef071..115fcff 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/api/MagixFormat.kt @@ -41,16 +41,16 @@ public fun MagixEndpoint.subscribe( public suspend fun MagixEndpoint.send( format: MagixFormat, payload: T, + source: String, target: String? = null, id: String? = null, parentId: String? = null, user: JsonElement? = null, - origin: String = format.defaultFormat, ) { val message = MagixMessage( format = format.defaultFormat, payload = magixJson.encodeToJsonElement(format.serializer, payload), - sourceEndpoint = origin, + sourceEndpoint = source, targetEndpoint = target, id = id, parentId = parentId, diff --git a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt index f6af81c..3272131 100644 --- a/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt +++ b/magix/magix-api/src/commonMain/kotlin/space/kscience/magix/services/MagixRegistry.kt @@ -20,13 +20,17 @@ import space.kscience.magix.api.subscribe */ public interface MagixRegistry { /** - * Request a property with name [propertyName] and user authentication data [user]. + * Request a property with name [propertyName]. * * Return a property value in its generic form or null if it is not present. * * Throw exception access is denied, or request failed. */ - public suspend fun request(propertyName: String, user: JsonElement? = null): JsonElement? + public suspend fun get(propertyName: String): JsonElement? +} + +public interface MutableMagixRegistry { + public suspend fun set(propertyName: String, value: JsonElement?, user: JsonElement?) } @Serializable @@ -48,7 +52,7 @@ public class MagixRegistryRequestMessage( @SerialName("registry.value") public class MagixRegistryValueMessage( override val propertyName: String, - public val value: JsonElement, + public val value: JsonElement?, ) : MagixRegistryMessage() @Serializable @@ -59,29 +63,58 @@ public class MagixRegistryErrorMessage( public val errorMessage: String? = null, ) : MagixRegistryMessage() +@Serializable +@SerialName("registry.modify") +public class MagixRegistryModifyMessage( + override val propertyName: String, + public val value: JsonElement, +) : MagixRegistryMessage() + /** * Launch a magix registry loop service based on local registry */ public fun CoroutineScope.launchMagixRegistry( + endpointName: String, endpoint: MagixEndpoint, registry: MagixRegistry, originFilter: Collection? = null, targetFilter: Collection? = null, ): Job = endpoint.subscribe(MagixRegistryMessage.format, originFilter, targetFilter) .onEach { (magixMessage, payload) -> - if (payload is MagixRegistryRequestMessage) { - try { - val value = registry.request(payload.propertyName, magixMessage.user) - endpoint.send( - MagixRegistryMessage.format, - MagixRegistryValueMessage(payload.propertyName, value ?: JsonNull) - ) - } catch (ex: Exception) { - endpoint.send( - MagixRegistryMessage.format, - MagixRegistryErrorMessage(payload.propertyName, ex::class.simpleName, ex.message) - ) + try { + when { + payload is MagixRegistryRequestMessage -> { + endpoint.send( + MagixRegistryMessage.format, + MagixRegistryValueMessage(payload.propertyName, registry.get(payload.propertyName) ?: JsonNull), + source = endpointName, + target = magixMessage.sourceEndpoint, + parentId = magixMessage.id + ) + } + + payload is MagixRegistryModifyMessage && registry is MutableMagixRegistry -> { + registry.set(payload.propertyName, payload.value, magixMessage.user) + // Broadcast updates. Do not set target + endpoint.send( + MagixRegistryMessage.format, + MagixRegistryValueMessage( + payload.propertyName, + registry.get(payload.propertyName) + ), + source = endpointName, + parentId = magixMessage.id + ) + } } + } catch (ex: Exception) { + endpoint.send( + MagixRegistryMessage.format, + MagixRegistryErrorMessage(payload.propertyName, ex::class.simpleName, ex.message), + source = endpointName, + target = magixMessage.sourceEndpoint, + parentId = magixMessage.id + ) } }.launchIn(this) @@ -92,20 +125,29 @@ public fun CoroutineScope.launchMagixRegistry( * The subscriber can terminate the flow at any moment to stop subscription, or use it indefinitely to continue observing changes. * To request a single value, use [Flow.first] function. * - * If [targetEndpoint] field is provided, send request only to given endpoint. + * If [registryEndpoint] field is provided, send request only to given endpoint. + * + * @param endpointName the name of endpoint requesting a property */ public suspend fun MagixEndpoint.getProperty( propertyName: String, + endpointName: String, user: JsonElement? = null, - targetEndpoint: String? = null, -): Flow> { - send(MagixRegistryMessage.format, MagixRegistryRequestMessage(propertyName), target = targetEndpoint, user = user) - return subscribe( + registryEndpoint: String? = null, +): Flow> = subscribe( + MagixRegistryMessage.format, + originFilter = registryEndpoint?.let { setOf(it) } +).mapNotNull { (message, response) -> + if (response is MagixRegistryValueMessage && response.propertyName == propertyName) { + message.sourceEndpoint to (response.value ?: return@mapNotNull null) + } else null +}.also { + //send the initial request after subscription + send( MagixRegistryMessage.format, - originFilter = targetEndpoint?.let { setOf(it) } - ).mapNotNull { (message, response) -> - if (response is MagixRegistryValueMessage && response.propertyName == propertyName) { - message.sourceEndpoint to response.value - } else null - } + MagixRegistryRequestMessage(propertyName), + source = endpointName, + target = registryEndpoint, + user = user + ) } \ No newline at end of file diff --git a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt index c0ec3f9..466fcb8 100644 --- a/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt +++ b/magix/magix-storage/src/commonMain/kotlin/space/kscience/magix/storage/historyEndpoint.kt @@ -50,11 +50,11 @@ public fun MagixEndpoint.launchHistory( send( format = MagixHistory.magixFormat, payload = sendPayload, + source = origin, target = request.sourceEndpoint, id = generateId(request), parentId = request.id, user = user, - origin = origin, ) } } diff --git a/settings.gradle.kts b/settings.gradle.kts index db083c4..04874d5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -60,7 +60,7 @@ include( ":magix:magix-mqtt", ":magix:magix-storage", ":magix:magix-storage:magix-storage-xodus", - ":controls-magix-client", + ":controls-magix", ":demo:all-things", ":demo:many-devices", ":demo:magix-demo", From 990e4794a135af232d6a264c71234fc8f8925c44 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 23 Aug 2023 16:21:11 +0300 Subject: [PATCH 42/43] All done for 0.2.0 release --- CHANGELOG.md | 32 ++++++++ README.md | 76 +++++++++++-------- build.gradle.kts | 2 +- controls-core/README.md | 22 ++---- controls-core/build.gradle.kts | 30 +++++++- controls-magix/README.md | 27 +++---- controls-magix/build.gradle.kts | 14 ++++ controls-modbus/README.md | 25 +++--- controls-modbus/build.gradle.kts | 25 ++++++ .../controls/modbus/DeviceProcessImage.kt | 6 +- controls-opcua/README.md | 23 +++--- controls-opcua/build.gradle.kts | 24 +++++- controls-pi/README.md | 19 ++--- controls-pi/build.gradle.kts | 4 + controls-ports-ktor/README.md | 19 ++--- controls-ports-ktor/build.gradle.kts | 10 +++ controls-serial/README.md | 19 ++--- controls-serial/build.gradle.kts | 8 ++ controls-server/README.md | 19 ++--- controls-server/build.gradle.kts | 8 +- controls-storage/README.md | 19 ++--- controls-storage/build.gradle.kts | 4 + controls-storage/controls-xodus/README.md | 19 ++--- .../controls-xodus/build.gradle.kts | 4 + .../kscience/controls/demo/MassDevice.kt | 13 +++- gradle.properties | 2 +- magix/README.md | 4 - magix/build.gradle.kts | 0 magix/magix-api/README.md | 19 ++--- magix/magix-api/build.gradle.kts | 9 +++ magix/magix-java-client/README.md | 32 -------- magix/magix-java-endpoint/README.md | 23 ++++++ .../build.gradle.kts | 9 +++ .../magix/client/JMagixEndpoint.java} | 10 +-- .../kscience/magix/client/KMagixEndpoint.kt} | 12 +-- magix/magix-mqtt/README.md | 17 +---- magix/magix-rabbit/README.md | 17 +---- magix/magix-rsocket/README.md | 17 +---- magix/magix-rsocket/build.gradle.kts | 6 ++ magix/magix-server/README.md | 17 +---- magix/magix-server/build.gradle.kts | 6 ++ magix/magix-storage/README.md | 17 +---- magix/magix-storage/build.gradle.kts | 6 ++ .../magix-storage-xodus/README.md | 17 +---- magix/magix-zmq/README.md | 17 +---- magix/magix-zmq/build.gradle.kts | 6 ++ settings.gradle.kts | 2 +- 47 files changed, 399 insertions(+), 337 deletions(-) create mode 100644 CHANGELOG.md delete mode 100644 magix/README.md delete mode 100644 magix/build.gradle.kts delete mode 100644 magix/magix-java-client/README.md create mode 100644 magix/magix-java-endpoint/README.md rename magix/{magix-java-client => magix-java-endpoint}/build.gradle.kts (79%) rename magix/{magix-java-client/src/main/java/space/kscience/magix/client/MagixClient.java => magix-java-endpoint/src/main/java/space/kscience/magix/client/JMagixEndpoint.java} (70%) rename magix/{magix-java-client/src/main/kotlin/space/kscience/magix/client/ControlsMagixClient.kt => magix-java-endpoint/src/main/kotlin/space/kscience/magix/client/KMagixEndpoint.kt} (81%) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..d6f9d13 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,32 @@ +# Changelog + +## Unreleased + +### Added +- Core interfaces for building a device server +- Magix service for binding controls devices (both as RPC client and server) +- A plugin for Controls-kt device server on top of modbus-rtu/modbus-tcp protocols +- A client and server connectors for OPC-UA via Eclipse Milo +- Implementation of byte ports on top os ktor-io asynchronous API +- Implementation of direct serial port communication with JSerialComm +- A combined Magix event loop server with web server for visualization. +- An API for stand-alone Controls-kt device or a hub. +- An implementation of controls-storage on top of JetBrains Xodus. +- A kotlin API for magix standard and some zero-dependency magix services +- Java API to work with magix endpoints without Kotlin +- MQTT client magix endpoint +- RabbitMQ client magix endpoint +- Magix endpoint (client) based on RSocket +- A magix event loop implementation in Kotlin. Includes HTTP/SSE and RSocket routes. +- Magix history database API +- ZMQ client endpoint for Magix + +### Changed + +### Deprecated + +### Removed + +### Fixed + +### Security diff --git a/README.md b/README.md index 9535868..d5b40c3 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Example view of a demo: ### [controls-core](controls-core) -> +> Core interfaces for building a device server > > **Maturity**: EXPERIMENTAL > @@ -51,140 +51,150 @@ Example view of a demo: > - [device](controls-core/src/commonMain/kotlin/space/kscience/controls/api/Device.kt) : Device API with subscription (asynchronous and pseudo-synchronous properties) > - [deviceMessage](controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt) : Specification for messages used to communicate between Controls-kt devices. > - [deviceHub](controls-core/src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt) : Grouping of devices into local tree-like hubs. +> - [deviceSpec](controls-core/src/commonMain/kotlin/space/kscience/controls/spec) : Mechanics and type-safe builders for devices. Including separation of device specification and device state. +> - [deviceManager](controls-core/src/commonMain/kotlin/space/kscience/controls/manager) : DataForge DI integration for devices. Includes device builders. +> - [ports](controls-core/src/commonMain/kotlin/space/kscience/controls/ports) : Working with asynchronous data sending and receiving raw byte arrays -### [controls-magix-client](controls-magix-client) -> +### [controls-magix](controls-magix) +> Magix service for binding controls devices (both as RPC client and server) > > **Maturity**: EXPERIMENTAL +> +> **Features:** +> - [controlsMagix](controls-magix/src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt) : Connect a `DeviceManage` with one or many devices to the Magix endpoint +> - [DeviceClient](controls-magix/src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt) : A remote connector to Controls-kt device via Magix + ### [controls-modbus](controls-modbus) -> +> A plugin for Controls-kt device server on top of modbus-rtu/modbus-tcp protocols > > **Maturity**: EXPERIMENTAL +> +> **Features:** +> - [modbusRegistryMap](controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt) : Type-safe modbus registry map. Allows to define both single-register and multi-register entries (using DataForge IO). +Automatically checks consistency. +> - [modbusProcessImage](controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt) : Binding of slave (server) modbus device to Controls-kt device +> - [modbusDevice](controls-modbus/src/main/kotlin/space/kscience/controls/modbus/ModbusDevice.kt) : A device with additional methods to work with modbus registers. + ### [controls-opcua](controls-opcua) -> +> A client and server connectors for OPC-UA via Eclipse Milo > > **Maturity**: EXPERIMENTAL +> +> **Features:** +> - [opcuaClient](controls-opcua/src/main/kotlin/space/kscience/controls/opcua/client) : Connect a Controls-kt as a client to OPC UA server +> - [opcuaServer](controls-opcua/src/main/kotlin/space/kscience/controls/opcua/server) : Create an OPC UA server on top of Controls-kt device (or device hub) + ### [controls-pi](controls-pi) -> +> Utils to work with controls-kt on Raspberry pi > > **Maturity**: EXPERIMENTAL ### [controls-ports-ktor](controls-ports-ktor) -> +> Implementation of byte ports on top os ktor-io asynchronous API > -> **Maturity**: EXPERIMENTAL +> **Maturity**: PROTOTYPE ### [controls-serial](controls-serial) -> +> Implementation of direct serial port communication with JSerialComm > > **Maturity**: EXPERIMENTAL ### [controls-server](controls-server) -> +> A combined Magix event loop server with web server for visualization. > -> **Maturity**: EXPERIMENTAL +> **Maturity**: PROTOTYPE ### [controls-storage](controls-storage) -> +> An API for stand-alone Controls-kt device or a hub. > > **Maturity**: PROTOTYPE ### [demo](demo) -> > > **Maturity**: EXPERIMENTAL ### [magix](magix) -> > > **Maturity**: EXPERIMENTAL ### [controls-storage/controls-xodus](controls-storage/controls-xodus) -> +> An implementation of controls-storage on top of JetBrains Xodus. > > **Maturity**: PROTOTYPE ### [demo/all-things](demo/all-things) -> > > **Maturity**: EXPERIMENTAL ### [demo/car](demo/car) -> > > **Maturity**: EXPERIMENTAL ### [demo/echo](demo/echo) -> > > **Maturity**: EXPERIMENTAL ### [demo/magix-demo](demo/magix-demo) -> > > **Maturity**: EXPERIMENTAL ### [demo/many-devices](demo/many-devices) -> > > **Maturity**: EXPERIMENTAL ### [demo/mks-pdr900](demo/mks-pdr900) -> > > **Maturity**: EXPERIMENTAL ### [demo/motors](demo/motors) -> > > **Maturity**: EXPERIMENTAL ### [magix/magix-api](magix/magix-api) -> +> A kotlin API for magix standard and some zero-dependency magix services > > **Maturity**: EXPERIMENTAL -### [magix/magix-java-client](magix/magix-java-client) -> +### [magix/magix-java-endpoint](magix/magix-java-endpoint) +> Java API to work with magix endpoints without Kotlin > > **Maturity**: EXPERIMENTAL ### [magix/magix-mqtt](magix/magix-mqtt) -> +> MQTT client magix endpoint > > **Maturity**: PROTOTYPE ### [magix/magix-rabbit](magix/magix-rabbit) -> +> RabbitMQ client magix endpoint > > **Maturity**: PROTOTYPE ### [magix/magix-rsocket](magix/magix-rsocket) -> +> Magix endpoint (client) based on RSocket > > **Maturity**: EXPERIMENTAL ### [magix/magix-server](magix/magix-server) -> +> A magix event loop implementation in Kotlin. Includes HTTP/SSE and RSocket routes. > > **Maturity**: EXPERIMENTAL ### [magix/magix-storage](magix/magix-storage) -> +> Magix history database API > -> **Maturity**: EXPERIMENTAL +> **Maturity**: PROTOTYPE ### [magix/magix-zmq](magix/magix-zmq) -> +> ZMQ client endpoint for Magix > > **Maturity**: EXPERIMENTAL ### [magix/magix-storage/magix-storage-xodus](magix/magix-storage/magix-storage-xodus) -> > > **Maturity**: PROTOTYPE diff --git a/build.gradle.kts b/build.gradle.kts index b854502..fda627d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,7 +13,7 @@ val xodusVersion by extra("2.0.1") allprojects { group = "space.kscience" - version = "0.2.0-dev-2" + version = "0.2.0" repositories{ maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") } diff --git a/controls-core/README.md b/controls-core/README.md index 1a8fa70..b75961d 100644 --- a/controls-core/README.md +++ b/controls-core/README.md @@ -1,39 +1,33 @@ # Module controls-core - +Core interfaces for building a device server ## Features - [device](src/commonMain/kotlin/space/kscience/controls/api/Device.kt) : Device API with subscription (asynchronous and pseudo-synchronous properties) - [deviceMessage](src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt) : Specification for messages used to communicate between Controls-kt devices. - [deviceHub](src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt) : Grouping of devices into local tree-like hubs. + - [deviceSpec](src/commonMain/kotlin/space/kscience/controls/spec) : Mechanics and type-safe builders for devices. Including separation of device specification and device state. + - [deviceManager](src/commonMain/kotlin/space/kscience/controls/manager) : DataForge DI integration for devices. Includes device builders. + - [ports](src/commonMain/kotlin/space/kscience/controls/ports) : Working with asynchronous data sending and receiving raw byte arrays ## Usage ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-core:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:controls-core:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:controls-core:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:controls-core:0.2.0-dev-2") + implementation("space.kscience:controls-core:0.2.0") } ``` diff --git a/controls-core/build.gradle.kts b/controls-core/build.gradle.kts index 41acc60..bbe32eb 100644 --- a/controls-core/build.gradle.kts +++ b/controls-core/build.gradle.kts @@ -1,8 +1,14 @@ +import space.kscience.gradle.Maturity + plugins { id("space.kscience.gradle.mpp") `maven-publish` } +description = """ + Core interfaces for building a device server +""".trimIndent() + val dataforgeVersion: String by rootProject.extra kscience { @@ -22,25 +28,41 @@ kscience { readme{ + maturity = Maturity.EXPERIMENTAL + feature("device", ref = "src/commonMain/kotlin/space/kscience/controls/api/Device.kt"){ """ Device API with subscription (asynchronous and pseudo-synchronous properties) """.trimIndent() } -} -readme{ feature("deviceMessage", ref = "src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt"){ """ Specification for messages used to communicate between Controls-kt devices. """.trimIndent() } -} -readme{ feature("deviceHub", ref = "src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt"){ """ Grouping of devices into local tree-like hubs. """.trimIndent() } + + feature("deviceSpec", ref = "src/commonMain/kotlin/space/kscience/controls/spec"){ + """ + Mechanics and type-safe builders for devices. Including separation of device specification and device state. + """.trimIndent() + } + + feature("deviceManager", ref = "src/commonMain/kotlin/space/kscience/controls/manager"){ + """ + DataForge DI integration for devices. Includes device builders. + """.trimIndent() + } + + feature("ports", ref = "src/commonMain/kotlin/space/kscience/controls/ports"){ + """ + Working with asynchronous data sending and receiving raw byte arrays + """.trimIndent() + } } \ No newline at end of file diff --git a/controls-magix/README.md b/controls-magix/README.md index eec6dc1..5473f02 100644 --- a/controls-magix/README.md +++ b/controls-magix/README.md @@ -1,32 +1,29 @@ -# Module controls-magix-client +# Module controls-magix + +Magix service for binding controls devices (both as RPC client and server) + +## Features + + - [controlsMagix](src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt) : Connect a `DeviceManage` with one or many devices to the Magix endpoint + - [DeviceClient](src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt) : A remote connector to Controls-kt device via Magix -Magix service for binding controls devices (both as RPC client and server ## Usage ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-magix-client:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:controls-magix:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:controls-magix-client:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:controls-magix-client:0.2.0-dev-2") + implementation("space.kscience:controls-magix:0.2.0") } ``` diff --git a/controls-magix/build.gradle.kts b/controls-magix/build.gradle.kts index 76c7956..0296b8c 100644 --- a/controls-magix/build.gradle.kts +++ b/controls-magix/build.gradle.kts @@ -1,3 +1,5 @@ +import space.kscience.gradle.Maturity + plugins { id("space.kscience.gradle.mpp") `maven-publish` @@ -21,5 +23,17 @@ kscience { } readme { + maturity = Maturity.EXPERIMENTAL + feature("controlsMagix", ref = "src/commonMain/kotlin/space/kscience/controls/client/controlsMagix.kt"){ + """ + Connect a `DeviceManage` with one or many devices to the Magix endpoint + """.trimIndent() + } + + feature("DeviceClient", ref = "src/commonMain/kotlin/space/kscience/controls/client/DeviceClient.kt"){ + """ + A remote connector to Controls-kt device via Magix + """.trimIndent() + } } \ No newline at end of file diff --git a/controls-modbus/README.md b/controls-modbus/README.md index 1e9a395..78d515a 100644 --- a/controls-modbus/README.md +++ b/controls-modbus/README.md @@ -2,31 +2,30 @@ A plugin for Controls-kt device server on top of modbus-rtu/modbus-tcp protocols +## Features + + - [modbusRegistryMap](src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt) : Type-safe modbus registry map. Allows to define both single-register and multi-register entries (using DataForge IO). +Automatically checks consistency. + - [modbusProcessImage](src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt) : Binding of slave (server) modbus device to Controls-kt device + - [modbusDevice](src/main/kotlin/space/kscience/controls/modbus/ModbusDevice.kt) : A device with additional methods to work with modbus registers. + + ## Usage ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-modbus:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:controls-modbus:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:controls-modbus:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:controls-modbus:0.2.0-dev-2") + implementation("space.kscience:controls-modbus:0.2.0") } ``` diff --git a/controls-modbus/build.gradle.kts b/controls-modbus/build.gradle.kts index 341d201..aee64d5 100644 --- a/controls-modbus/build.gradle.kts +++ b/controls-modbus/build.gradle.kts @@ -1,3 +1,5 @@ +import space.kscience.gradle.Maturity + plugins { id("space.kscience.gradle.jvm") `maven-publish` @@ -12,3 +14,26 @@ dependencies { api(projects.controlsCore) api("com.ghgande:j2mod:3.1.1") } + +readme{ + maturity = Maturity.EXPERIMENTAL + + feature("modbusRegistryMap", ref = "src/main/kotlin/space/kscience/controls/modbus/ModbusRegistryMap.kt"){ + """ + Type-safe modbus registry map. Allows to define both single-register and multi-register entries (using DataForge IO). + Automatically checks consistency. + """.trimIndent() + } + + feature("modbusProcessImage", ref = "src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt"){ + """ + Binding of slave (server) modbus device to Controls-kt device + """.trimIndent() + } + + feature("modbusDevice", ref = "src/main/kotlin/space/kscience/controls/modbus/ModbusDevice.kt"){ + """ + A device with additional methods to work with modbus registers. + """.trimIndent() + } +} \ No newline at end of file diff --git a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt index 3352f1a..2f56a8a 100644 --- a/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt +++ b/controls-modbus/src/main/kotlin/space/kscience/controls/modbus/DeviceProcessImage.kt @@ -12,7 +12,7 @@ import space.kscience.controls.spec.set import space.kscience.controls.spec.useProperty -public class DeviceProcessImageBuilder( +public class DeviceProcessImageBuilder internal constructor( private val device: D, public val image: ProcessImageImplementation, ) { @@ -201,7 +201,9 @@ public class DeviceProcessImageBuilder( } - +/** + * Bind the device to Modbus slave (server) image. + */ public fun D.bindProcessImage( openOnBind: Boolean = true, binding: DeviceProcessImageBuilder.() -> Unit, diff --git a/controls-opcua/README.md b/controls-opcua/README.md index a365cb2..8cdd373 100644 --- a/controls-opcua/README.md +++ b/controls-opcua/README.md @@ -1,32 +1,29 @@ # Module controls-opcua +A client and server connectors for OPC-UA via Eclipse Milo + +## Features + + - [opcuaClient](src/main/kotlin/space/kscience/controls/opcua/client) : Connect a Controls-kt as a client to OPC UA server + - [opcuaServer](src/main/kotlin/space/kscience/controls/opcua/server) : Create an OPC UA server on top of Controls-kt device (or device hub) ## Usage ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-opcua:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:controls-opcua:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:controls-opcua:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:controls-opcua:0.2.0-dev-2") + implementation("space.kscience:controls-opcua:0.2.0") } ``` diff --git a/controls-opcua/build.gradle.kts b/controls-opcua/build.gradle.kts index b0530c7..e7e1da8 100644 --- a/controls-opcua/build.gradle.kts +++ b/controls-opcua/build.gradle.kts @@ -1,11 +1,17 @@ +import space.kscience.gradle.Maturity + plugins { id("space.kscience.gradle.jvm") `maven-publish` } +description = """ + A client and server connectors for OPC-UA via Eclipse Milo +""".trimIndent() + val ktorVersion: String by rootProject.extra -val miloVersion: String = "0.6.9" +val miloVersion: String = "0.6.10" dependencies { api(projects.controlsCore) @@ -17,3 +23,19 @@ dependencies { testImplementation(spclibs.kotlinx.coroutines.test) } + +readme{ + maturity = Maturity.EXPERIMENTAL + + feature("opcuaClient", ref = "src/main/kotlin/space/kscience/controls/opcua/client"){ + """ + Connect a Controls-kt as a client to OPC UA server + """.trimIndent() + } + + feature("opcuaServer", ref = "src/main/kotlin/space/kscience/controls/opcua/server"){ + """ + Create an OPC UA server on top of Controls-kt device (or device hub) + """.trimIndent() + } +} diff --git a/controls-pi/README.md b/controls-pi/README.md index 9d91bcf..cd9ee0a 100644 --- a/controls-pi/README.md +++ b/controls-pi/README.md @@ -1,32 +1,23 @@ # Module controls-pi - +Utils to work with controls-kt on Raspberry pi ## Usage ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-pi:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:controls-pi:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:controls-pi:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:controls-pi:0.2.0-dev-2") + implementation("space.kscience:controls-pi:0.2.0") } ``` diff --git a/controls-pi/build.gradle.kts b/controls-pi/build.gradle.kts index b269c85..a763396 100644 --- a/controls-pi/build.gradle.kts +++ b/controls-pi/build.gradle.kts @@ -3,6 +3,10 @@ plugins { `maven-publish` } +description = """ + Utils to work with controls-kt on Raspberry pi +""".trimIndent() + dependencies{ api(project(":controls-core")) api("com.pi4j:pi4j-ktx:2.4.0") // Kotlin DSL diff --git a/controls-ports-ktor/README.md b/controls-ports-ktor/README.md index b43e48c..7f935f9 100644 --- a/controls-ports-ktor/README.md +++ b/controls-ports-ktor/README.md @@ -1,32 +1,23 @@ # Module controls-ports-ktor - +Implementation of byte ports on top os ktor-io asynchronous API ## Usage ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-ports-ktor:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:controls-ports-ktor:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:controls-ports-ktor:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:controls-ports-ktor:0.2.0-dev-2") + implementation("space.kscience:controls-ports-ktor:0.2.0") } ``` diff --git a/controls-ports-ktor/build.gradle.kts b/controls-ports-ktor/build.gradle.kts index 93e376c..4c8ad9a 100644 --- a/controls-ports-ktor/build.gradle.kts +++ b/controls-ports-ktor/build.gradle.kts @@ -1,11 +1,21 @@ +import space.kscience.gradle.Maturity + plugins { id("space.kscience.gradle.jvm") `maven-publish` } +description = """ + Implementation of byte ports on top os ktor-io asynchronous API +""".trimIndent() + val ktorVersion: String by rootProject.extra dependencies { api(projects.controlsCore) api("io.ktor:ktor-network:$ktorVersion") } + +readme{ + maturity = Maturity.PROTOTYPE +} diff --git a/controls-serial/README.md b/controls-serial/README.md index bb2405a..209055d 100644 --- a/controls-serial/README.md +++ b/controls-serial/README.md @@ -1,32 +1,23 @@ # Module controls-serial - +Implementation of direct serial port communication with JSerialComm ## Usage ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-serial:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:controls-serial:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:controls-serial:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:controls-serial:0.2.0-dev-2") + implementation("space.kscience:controls-serial:0.2.0") } ``` diff --git a/controls-serial/build.gradle.kts b/controls-serial/build.gradle.kts index 006a7c2..a9afc41 100644 --- a/controls-serial/build.gradle.kts +++ b/controls-serial/build.gradle.kts @@ -1,9 +1,17 @@ +import space.kscience.gradle.Maturity + plugins { id("space.kscience.gradle.jvm") `maven-publish` } +description = "Implementation of direct serial port communication with JSerialComm" + dependencies{ api(project(":controls-core")) implementation("com.fazecast:jSerialComm:2.10.3") +} + +readme{ + maturity = Maturity.EXPERIMENTAL } \ No newline at end of file diff --git a/controls-server/README.md b/controls-server/README.md index e2218a0..83408e8 100644 --- a/controls-server/README.md +++ b/controls-server/README.md @@ -1,32 +1,23 @@ # Module controls-server -A magix event loop server with web server for visualization. +A combined Magix event loop server with web server for visualization. ## Usage ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-server:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:controls-server:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:controls-server:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:controls-server:0.2.0-dev-2") + implementation("space.kscience:controls-server:0.2.0") } ``` diff --git a/controls-server/build.gradle.kts b/controls-server/build.gradle.kts index 2bf9b01..43a9d61 100644 --- a/controls-server/build.gradle.kts +++ b/controls-server/build.gradle.kts @@ -1,10 +1,12 @@ +import space.kscience.gradle.Maturity + plugins { id("space.kscience.gradle.jvm") `maven-publish` } description = """ - A magix event loop server with web server for visualization. + A combined Magix event loop server with web server for visualization. """.trimIndent() val dataforgeVersion: String by rootProject.extra @@ -20,4 +22,8 @@ dependencies { implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") implementation("io.ktor:ktor-server-html-builder:$ktorVersion") implementation("io.ktor:ktor-server-status-pages:$ktorVersion") +} + +readme{ + maturity = Maturity.PROTOTYPE } \ No newline at end of file diff --git a/controls-storage/README.md b/controls-storage/README.md index a70b2e2..51cdbcb 100644 --- a/controls-storage/README.md +++ b/controls-storage/README.md @@ -1,32 +1,23 @@ # Module controls-storage - +An API for stand-alone Controls-kt device or a hub. ## Usage ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-storage:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:controls-storage:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:controls-storage:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:controls-storage:0.2.0-dev-2") + implementation("space.kscience:controls-storage:0.2.0") } ``` diff --git a/controls-storage/build.gradle.kts b/controls-storage/build.gradle.kts index a8c6c29..b3d8c15 100644 --- a/controls-storage/build.gradle.kts +++ b/controls-storage/build.gradle.kts @@ -5,6 +5,10 @@ plugins { val dataforgeVersion: String by rootProject.extra +description = """ + An API for stand-alone Controls-kt device or a hub. +""".trimIndent() + kscience{ jvm() js() diff --git a/controls-storage/controls-xodus/README.md b/controls-storage/controls-xodus/README.md index cb7b761..790b356 100644 --- a/controls-storage/controls-xodus/README.md +++ b/controls-storage/controls-xodus/README.md @@ -1,32 +1,23 @@ # Module controls-xodus - +An implementation of controls-storage on top of JetBrains Xodus. ## Usage ## Artifact: -The Maven coordinates of this project are `space.kscience:controls-xodus:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:controls-xodus:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:controls-xodus:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:controls-xodus:0.2.0-dev-2") + implementation("space.kscience:controls-xodus:0.2.0") } ``` diff --git a/controls-storage/controls-xodus/build.gradle.kts b/controls-storage/controls-xodus/build.gradle.kts index 635f8e3..329f3ce 100644 --- a/controls-storage/controls-xodus/build.gradle.kts +++ b/controls-storage/controls-xodus/build.gradle.kts @@ -5,6 +5,10 @@ plugins { val xodusVersion: String by rootProject.extra +description = """ + An implementation of controls-storage on top of JetBrains Xodus. +""".trimIndent() + dependencies { api(projects.controlsStorage) implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") diff --git a/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt b/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt index 94ef9db..5c89c26 100644 --- a/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt +++ b/demo/many-devices/src/main/kotlin/space/kscience/controls/demo/MassDevice.kt @@ -33,6 +33,7 @@ import space.kscience.plotly.server.show import space.kscince.magix.zmq.ZmqMagixFlowPlugin import kotlin.random.Random import kotlin.time.Duration +import kotlin.time.Duration.Companion.ZERO import kotlin.time.Duration.Companion.milliseconds @@ -48,7 +49,7 @@ class MassDevice(context: Context, meta: Meta) : DeviceBySpec(MassDe val value by doubleProperty { randomValue } override suspend fun MassDevice.onOpen() { - doRecurring(10.milliseconds) { + doRecurring((meta["delay"].int ?: 10).milliseconds) { read(value) } } @@ -68,6 +69,7 @@ suspend fun main() { repeat(numDevices) { context.launch(newFixedThreadPoolContext(2, "Device${it}")) { + delay(1) val deviceContext = Context("Device${it}") { plugin(DeviceManager) } @@ -89,6 +91,8 @@ suspend fun main() { plot(renderer = container) { layout { title = "Latest event" + xaxis.title = "Device number" + yaxis.title = "Maximum latency in ms" } bar { launch(Dispatchers.IO) { @@ -97,18 +101,23 @@ suspend fun main() { val mutex = Mutex() val latest = HashMap() + val max = HashMap() monitorEndpoint.subscribe(DeviceManager.magixFormat).onEach { (magixMessage, payload) -> mutex.withLock { + val delay = Clock.System.now() - payload.time!! latest[magixMessage.sourceEndpoint] = Clock.System.now() - payload.time!! + max[magixMessage.sourceEndpoint] = + maxOf(delay, max[magixMessage.sourceEndpoint] ?: ZERO) } }.launchIn(this) while (isActive) { delay(200) mutex.withLock { - val sorted = latest.mapKeys { it.key.substring(6).toInt() }.toSortedMap() + val sorted = max.mapKeys { it.key.substring(6).toInt() }.toSortedMap() latest.clear() + max.clear() x.numbers = sorted.keys y.numbers = sorted.values.map { it.inWholeMilliseconds / 1000.0 + 0.0001 } } diff --git a/gradle.properties b/gradle.properties index 5c5294c..5b956f1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -10,4 +10,4 @@ publishing.sonatype=false org.gradle.configureondemand=true org.gradle.jvmargs=-Xmx4096m -toolsVersion=0.14.8-kotlin-1.8.20 \ No newline at end of file +toolsVersion=0.14.10-kotlin-1.9.0 \ No newline at end of file diff --git a/magix/README.md b/magix/README.md deleted file mode 100644 index de1d7a3..0000000 --- a/magix/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Module magix - - - diff --git a/magix/build.gradle.kts b/magix/build.gradle.kts deleted file mode 100644 index e69de29..0000000 diff --git a/magix/magix-api/README.md b/magix/magix-api/README.md index e1207ab..ee00c53 100644 --- a/magix/magix-api/README.md +++ b/magix/magix-api/README.md @@ -1,32 +1,23 @@ # Module magix-api - +A kotlin API for magix standard and some zero-dependency magix services ## Usage ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-api:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:magix-api:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:magix-api:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:magix-api:0.2.0-dev-2") + implementation("space.kscience:magix-api:0.2.0") } ``` diff --git a/magix/magix-api/build.gradle.kts b/magix/magix-api/build.gradle.kts index 5f8bdf3..159989d 100644 --- a/magix/magix-api/build.gradle.kts +++ b/magix/magix-api/build.gradle.kts @@ -1,8 +1,14 @@ +import space.kscience.gradle.Maturity + plugins { id("space.kscience.gradle.mpp") `maven-publish` } +description = """ + A kotlin API for magix standard and some zero-dependency magix services +""".trimIndent() + kscience { jvm() js() @@ -13,3 +19,6 @@ kscience { } } +readme{ + maturity = Maturity.EXPERIMENTAL +} diff --git a/magix/magix-java-client/README.md b/magix/magix-java-client/README.md deleted file mode 100644 index a81787e..0000000 --- a/magix/magix-java-client/README.md +++ /dev/null @@ -1,32 +0,0 @@ -# Module magix-java-client - - - -## Usage - -## Artifact: - -The Maven coordinates of this project are `space.kscience:magix-java-client:0.2.0-dev-2`. - -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:magix-java-client:0.2.0-dev-2' -} -``` -**Gradle Kotlin DSL:** -```kotlin -repositories { - maven("https://repo.kotlin.link") - mavenCentral() -} - -dependencies { - implementation("space.kscience:magix-java-client:0.2.0-dev-2") -} -``` diff --git a/magix/magix-java-endpoint/README.md b/magix/magix-java-endpoint/README.md new file mode 100644 index 0000000..abcaa6f --- /dev/null +++ b/magix/magix-java-endpoint/README.md @@ -0,0 +1,23 @@ +# Module magix-java-endpoint + +Java API to work with magix endpoints without Kotlin + +## Usage + +## Artifact: + +The Maven coordinates of this project are `space.kscience:magix-java-endpoint:0.2.0`. + +**Gradle Kotlin DSL:** +```kotlin +repositories { + maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") + mavenCentral() +} + +dependencies { + implementation("space.kscience:magix-java-endpoint:0.2.0") +} +``` diff --git a/magix/magix-java-client/build.gradle.kts b/magix/magix-java-endpoint/build.gradle.kts similarity index 79% rename from magix/magix-java-client/build.gradle.kts rename to magix/magix-java-endpoint/build.gradle.kts index 63041b3..ff51835 100644 --- a/magix/magix-java-client/build.gradle.kts +++ b/magix/magix-java-endpoint/build.gradle.kts @@ -1,5 +1,6 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import space.kscience.gradle.KScienceVersions +import space.kscience.gradle.Maturity plugins { java @@ -7,6 +8,10 @@ plugins { `maven-publish` } +description = """ + Java API to work with magix endpoints without Kotlin +""".trimIndent() + dependencies { implementation(project(":magix:magix-rsocket")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:${KScienceVersions.coroutinesVersion}") @@ -23,4 +28,8 @@ tasks.withType{ kotlinOptions { freeCompilerArgs -= "-Xjdk-release=11" } +} + +readme{ + maturity = Maturity.EXPERIMENTAL } \ No newline at end of file diff --git a/magix/magix-java-client/src/main/java/space/kscience/magix/client/MagixClient.java b/magix/magix-java-endpoint/src/main/java/space/kscience/magix/client/JMagixEndpoint.java similarity index 70% rename from magix/magix-java-client/src/main/java/space/kscience/magix/client/MagixClient.java rename to magix/magix-java-endpoint/src/main/java/space/kscience/magix/client/JMagixEndpoint.java index 3270544..08d0a26 100644 --- a/magix/magix-java-client/src/main/java/space/kscience/magix/client/MagixClient.java +++ b/magix/magix-java-endpoint/src/main/java/space/kscience/magix/client/JMagixEndpoint.java @@ -11,7 +11,7 @@ import java.util.concurrent.Flow; * * @param */ -public interface MagixClient { +public interface JMagixEndpoint { void broadcast(MagixMessage msg) throws IOException; Flow.Publisher subscribe(); @@ -22,8 +22,8 @@ public interface MagixClient { * @param port port of magix server event loop * @return the client */ - static MagixClient rSocketTcp(String host, int port) { - return ControlsMagixClient.Companion.rSocketTcp(host, port); + static JMagixEndpoint rSocketTcp(String host, int port) { + return KMagixEndpoint.Companion.rSocketTcp(host, port); } /** @@ -32,7 +32,7 @@ public interface MagixClient { * @param port port of magix server event loop * @param path context path for WS connection */ - static MagixClient rSocketWs(String host, int port, String path) { - return ControlsMagixClient.Companion.rSocketWs(host, port, path); + static JMagixEndpoint rSocketWs(String host, int port, String path) { + return KMagixEndpoint.Companion.rSocketWs(host, port, path); } } diff --git a/magix/magix-java-client/src/main/kotlin/space/kscience/magix/client/ControlsMagixClient.kt b/magix/magix-java-endpoint/src/main/kotlin/space/kscience/magix/client/KMagixEndpoint.kt similarity index 81% rename from magix/magix-java-client/src/main/kotlin/space/kscience/magix/client/ControlsMagixClient.kt rename to magix/magix-java-endpoint/src/main/kotlin/space/kscience/magix/client/KMagixEndpoint.kt index 1efa746..2d20d8f 100644 --- a/magix/magix-java-client/src/main/kotlin/space/kscience/magix/client/ControlsMagixClient.kt +++ b/magix/magix-java-endpoint/src/main/kotlin/space/kscience/magix/client/KMagixEndpoint.kt @@ -9,10 +9,10 @@ import space.kscience.magix.rsocket.rSocketWithTcp import space.kscience.magix.rsocket.rSocketWithWebSockets import java.util.concurrent.Flow -internal class ControlsMagixClient( +internal class KMagixEndpoint( private val endpoint: MagixEndpoint, private val filter: MagixMessageFilter, -) : MagixClient { +) : JMagixEndpoint { override fun broadcast(msg: MagixMessage): Unit = runBlocking { endpoint.broadcast(msg) @@ -25,22 +25,22 @@ internal class ControlsMagixClient( fun rSocketTcp( host: String, port: Int, - ): ControlsMagixClient { + ): KMagixEndpoint { val endpoint = runBlocking { MagixEndpoint.rSocketWithTcp(host, port) } - return ControlsMagixClient(endpoint, MagixMessageFilter()) + return KMagixEndpoint(endpoint, MagixMessageFilter()) } fun rSocketWs( host: String, port: Int, path: String = "/rsocket" - ): ControlsMagixClient { + ): KMagixEndpoint { val endpoint = runBlocking { MagixEndpoint.rSocketWithWebSockets(host, port, path) } - return ControlsMagixClient(endpoint, MagixMessageFilter()) + return KMagixEndpoint(endpoint, MagixMessageFilter()) } } } \ No newline at end of file diff --git a/magix/magix-mqtt/README.md b/magix/magix-mqtt/README.md index 061803e..6c34fdc 100644 --- a/magix/magix-mqtt/README.md +++ b/magix/magix-mqtt/README.md @@ -6,27 +6,18 @@ MQTT client magix endpoint ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-mqtt:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:magix-mqtt:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:magix-mqtt:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:magix-mqtt:0.2.0-dev-2") + implementation("space.kscience:magix-mqtt:0.2.0") } ``` diff --git a/magix/magix-rabbit/README.md b/magix/magix-rabbit/README.md index 79b0af1..7fc42ad 100644 --- a/magix/magix-rabbit/README.md +++ b/magix/magix-rabbit/README.md @@ -6,27 +6,18 @@ RabbitMQ client magix endpoint ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-rabbit:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:magix-rabbit:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:magix-rabbit:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:magix-rabbit:0.2.0-dev-2") + implementation("space.kscience:magix-rabbit:0.2.0") } ``` diff --git a/magix/magix-rsocket/README.md b/magix/magix-rsocket/README.md index 1307fbe..799717d 100644 --- a/magix/magix-rsocket/README.md +++ b/magix/magix-rsocket/README.md @@ -6,27 +6,18 @@ Magix endpoint (client) based on RSocket ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-rsocket:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:magix-rsocket:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:magix-rsocket:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:magix-rsocket:0.2.0-dev-2") + implementation("space.kscience:magix-rsocket:0.2.0") } ``` diff --git a/magix/magix-rsocket/build.gradle.kts b/magix/magix-rsocket/build.gradle.kts index 39aa8aa..b2046bb 100644 --- a/magix/magix-rsocket/build.gradle.kts +++ b/magix/magix-rsocket/build.gradle.kts @@ -1,3 +1,5 @@ +import space.kscience.gradle.Maturity + plugins { id("space.kscience.gradle.mpp") `maven-publish` @@ -35,4 +37,8 @@ kotlin { } } } +} + +readme { + maturity = Maturity.EXPERIMENTAL } \ No newline at end of file diff --git a/magix/magix-server/README.md b/magix/magix-server/README.md index 0ce680b..27d97e0 100644 --- a/magix/magix-server/README.md +++ b/magix/magix-server/README.md @@ -6,27 +6,18 @@ A magix event loop implementation in Kotlin. Includes HTTP/SSE and RSocket route ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-server:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:magix-server:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:magix-server:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:magix-server:0.2.0-dev-2") + implementation("space.kscience:magix-server:0.2.0") } ``` diff --git a/magix/magix-server/build.gradle.kts b/magix/magix-server/build.gradle.kts index 4a3b102..cb63049 100644 --- a/magix/magix-server/build.gradle.kts +++ b/magix/magix-server/build.gradle.kts @@ -1,3 +1,5 @@ +import space.kscience.gradle.Maturity + plugins { id("space.kscience.gradle.jvm") `maven-publish` @@ -28,4 +30,8 @@ dependencies{ api("io.rsocket.kotlin:rsocket-ktor-server:$rsocketVersion") api("io.rsocket.kotlin:rsocket-transport-ktor-tcp:$rsocketVersion") +} + +readme{ + maturity = Maturity.EXPERIMENTAL } \ No newline at end of file diff --git a/magix/magix-storage/README.md b/magix/magix-storage/README.md index fd9822f..fa367b4 100644 --- a/magix/magix-storage/README.md +++ b/magix/magix-storage/README.md @@ -6,27 +6,18 @@ Magix history database API ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-storage:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:magix-storage:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:magix-storage:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:magix-storage:0.2.0-dev-2") + implementation("space.kscience:magix-storage:0.2.0") } ``` diff --git a/magix/magix-storage/build.gradle.kts b/magix/magix-storage/build.gradle.kts index 76bc2df..cb0bc24 100644 --- a/magix/magix-storage/build.gradle.kts +++ b/magix/magix-storage/build.gradle.kts @@ -1,3 +1,5 @@ +import space.kscience.gradle.Maturity + plugins { id("space.kscience.gradle.mpp") `maven-publish` @@ -21,3 +23,7 @@ kscience { api(spclibs.kotlinx.datetime) } } + +readme{ + maturity = Maturity.PROTOTYPE +} diff --git a/magix/magix-storage/magix-storage-xodus/README.md b/magix/magix-storage/magix-storage-xodus/README.md index bcc8a6e..b3d871b 100644 --- a/magix/magix-storage/magix-storage-xodus/README.md +++ b/magix/magix-storage/magix-storage-xodus/README.md @@ -6,27 +6,18 @@ ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-storage-xodus:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:magix-storage-xodus:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:magix-storage-xodus:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:magix-storage-xodus:0.2.0-dev-2") + implementation("space.kscience:magix-storage-xodus:0.2.0") } ``` diff --git a/magix/magix-zmq/README.md b/magix/magix-zmq/README.md index 65c4892..1338cce 100644 --- a/magix/magix-zmq/README.md +++ b/magix/magix-zmq/README.md @@ -6,27 +6,18 @@ ZMQ client endpoint for Magix ## Artifact: -The Maven coordinates of this project are `space.kscience:magix-zmq:0.2.0-dev-2`. +The Maven coordinates of this project are `space.kscience:magix-zmq:0.2.0`. -**Gradle Groovy:** -```groovy -repositories { - maven { url 'https://repo.kotlin.link' } - mavenCentral() -} - -dependencies { - implementation 'space.kscience:magix-zmq:0.2.0-dev-2' -} -``` **Gradle Kotlin DSL:** ```kotlin repositories { maven("https://repo.kotlin.link") + //uncomment to access development builds + //maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") mavenCentral() } dependencies { - implementation("space.kscience:magix-zmq:0.2.0-dev-2") + implementation("space.kscience:magix-zmq:0.2.0") } ``` diff --git a/magix/magix-zmq/build.gradle.kts b/magix/magix-zmq/build.gradle.kts index 3258e17..cf4ee9b 100644 --- a/magix/magix-zmq/build.gradle.kts +++ b/magix/magix-zmq/build.gradle.kts @@ -1,3 +1,5 @@ +import space.kscience.gradle.Maturity + plugins { id("space.kscience.gradle.jvm") `maven-publish` @@ -12,3 +14,7 @@ dependencies { api("org.slf4j:slf4j-api:2.0.6") api("org.zeromq:jeromq:0.5.2") } + +readme { + maturity = Maturity.EXPERIMENTAL +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 04874d5..20ac44e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -54,7 +54,7 @@ include( ":magix:magix-api", ":magix:magix-server", ":magix:magix-rsocket", - ":magix:magix-java-client", + ":magix:magix-java-endpoint", ":magix:magix-zmq", ":magix:magix-rabbit", ":magix:magix-mqtt", From 61af2780ed52491178794775530c4ddbb246e9cb Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Wed, 23 Aug 2023 16:23:27 +0300 Subject: [PATCH 43/43] fix repositores --- build.gradle.kts | 3 +- controls-core/api/controls-core.api | 915 ++++++++++++++++++ controls-magix/api/controls-magix.api | 199 ++++ controls-modbus/api/controls-modbus.api | 166 ++++ controls-opcua/api/controls-opcua.api | 82 ++ controls-pi/api/controls-pi.api | 28 + .../api/controls-ports-ktor.api | 47 + controls-serial/api/controls-serial.api | 29 + controls-server/api/controls-server.api | 9 + demo/all-things/api/all-things.api | 77 ++ demo/car/api/car.api | 111 +++ demo/echo/api/echo.api | 5 + demo/magix-demo/api/magix-demo.api | 7 + demo/many-devices/api/many-devices.api | 18 + demo/mks-pdr900/api/mks-pdr900.api | 28 + demo/motors/api/motors.api | 115 +++ magix/magix-api/api/magix-api.api | 271 ++++++ .../api/magix-java-client.api | 7 + magix/magix-rsocket/api/magix-rsocket.api | 37 + magix/magix-server/api/magix-server.api | 45 + magix/magix-storage/api/magix-storage.api | 183 ++++ magix/magix-zmq/api/magix-zmq.api | 23 + 22 files changed, 2403 insertions(+), 2 deletions(-) create mode 100644 controls-core/api/controls-core.api create mode 100644 controls-magix/api/controls-magix.api create mode 100644 controls-modbus/api/controls-modbus.api create mode 100644 controls-opcua/api/controls-opcua.api create mode 100644 controls-pi/api/controls-pi.api create mode 100644 controls-ports-ktor/api/controls-ports-ktor.api create mode 100644 controls-serial/api/controls-serial.api create mode 100644 controls-server/api/controls-server.api create mode 100644 demo/all-things/api/all-things.api create mode 100644 demo/car/api/car.api create mode 100644 demo/echo/api/echo.api create mode 100644 demo/magix-demo/api/magix-demo.api create mode 100644 demo/many-devices/api/many-devices.api create mode 100644 demo/mks-pdr900/api/mks-pdr900.api create mode 100644 demo/motors/api/motors.api create mode 100644 magix/magix-api/api/magix-api.api create mode 100644 magix/magix-java-endpoint/api/magix-java-client.api create mode 100644 magix/magix-rsocket/api/magix-rsocket.api create mode 100644 magix/magix-server/api/magix-server.api create mode 100644 magix/magix-storage/api/magix-storage.api create mode 100644 magix/magix-zmq/api/magix-zmq.api diff --git a/build.gradle.kts b/build.gradle.kts index fda627d..df7c664 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,10 +29,9 @@ ksciencePublish { if (isInDevelopment) { "https://maven.pkg.jetbrains.space/spc/p/sci/dev" } else { - "https://maven.pkg.jetbrains.space/spc/p/sci/release" + "https://maven.pkg.jetbrains.space/spc/p/sci/maven" } ) - space("https://maven.pkg.jetbrains.space/spc/p/controls/maven") } readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md") \ No newline at end of file diff --git a/controls-core/api/controls-core.api b/controls-core/api/controls-core.api new file mode 100644 index 0000000..17a8c3a --- /dev/null +++ b/controls-core/api/controls-core.api @@ -0,0 +1,915 @@ +public final class space/kscience/controls/api/ActionDescriptor { + public static final field Companion Lspace/kscience/controls/api/ActionDescriptor$Companion; + public synthetic fun (ILjava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;)V + public final fun getInfo ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public final fun setInfo (Ljava/lang/String;)V + public static final synthetic fun write$Self (Lspace/kscience/controls/api/ActionDescriptor;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/ActionDescriptor$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/ActionDescriptor$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/ActionDescriptor; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/ActionDescriptor;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/ActionDescriptor$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/ActionExecuteMessage : space/kscience/controls/api/DeviceMessage { + public static final field Companion Lspace/kscience/controls/api/ActionExecuteMessage$Companion; + public synthetic fun (ILjava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V + public synthetic fun (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun changeSource (Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/api/DeviceMessage; + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lspace/kscience/dataforge/meta/Meta; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Lspace/kscience/dataforge/names/Name; + public final fun component5 ()Lspace/kscience/dataforge/names/Name; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Lkotlinx/datetime/Instant; + public final fun copy (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)Lspace/kscience/controls/api/ActionExecuteMessage; + public static synthetic fun copy$default (Lspace/kscience/controls/api/ActionExecuteMessage;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILjava/lang/Object;)Lspace/kscience/controls/api/ActionExecuteMessage; + public fun equals (Ljava/lang/Object;)Z + public final fun getAction ()Ljava/lang/String; + public final fun getArgument ()Lspace/kscience/dataforge/meta/Meta; + public fun getComment ()Ljava/lang/String; + public final fun getRequestId ()Ljava/lang/String; + public fun getSourceDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTargetDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTime ()Lkotlinx/datetime/Instant; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/api/ActionExecuteMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/ActionExecuteMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/ActionExecuteMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/ActionExecuteMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/ActionExecuteMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/ActionExecuteMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/ActionResultMessage : space/kscience/controls/api/DeviceMessage { + public static final field Companion Lspace/kscience/controls/api/ActionResultMessage$Companion; + public synthetic fun (ILjava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V + public synthetic fun (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun changeSource (Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/api/DeviceMessage; + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lspace/kscience/dataforge/meta/Meta; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Lspace/kscience/dataforge/names/Name; + public final fun component5 ()Lspace/kscience/dataforge/names/Name; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Lkotlinx/datetime/Instant; + public final fun copy (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)Lspace/kscience/controls/api/ActionResultMessage; + public static synthetic fun copy$default (Lspace/kscience/controls/api/ActionResultMessage;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILjava/lang/Object;)Lspace/kscience/controls/api/ActionResultMessage; + public fun equals (Ljava/lang/Object;)Z + public final fun getAction ()Ljava/lang/String; + public fun getComment ()Ljava/lang/String; + public final fun getRequestId ()Ljava/lang/String; + public final fun getResult ()Lspace/kscience/dataforge/meta/Meta; + public fun getSourceDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTargetDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTime ()Lkotlinx/datetime/Instant; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/api/ActionResultMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/ActionResultMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/ActionResultMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/ActionResultMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/ActionResultMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/ActionResultMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/BinaryNotificationMessage : space/kscience/controls/api/DeviceMessage { + public static final field Companion Lspace/kscience/controls/api/BinaryNotificationMessage$Companion; + public synthetic fun (ILjava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V + public synthetic fun (Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun changeSource (Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/api/DeviceMessage; + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lspace/kscience/dataforge/names/Name; + public final fun component3 ()Lspace/kscience/dataforge/names/Name; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Lkotlinx/datetime/Instant; + public final fun copy (Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)Lspace/kscience/controls/api/BinaryNotificationMessage; + public static synthetic fun copy$default (Lspace/kscience/controls/api/BinaryNotificationMessage;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILjava/lang/Object;)Lspace/kscience/controls/api/BinaryNotificationMessage; + public fun equals (Ljava/lang/Object;)Z + public final fun getBinaryID ()Ljava/lang/String; + public fun getComment ()Ljava/lang/String; + public fun getSourceDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTargetDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTime ()Lkotlinx/datetime/Instant; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/api/BinaryNotificationMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/BinaryNotificationMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/BinaryNotificationMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/BinaryNotificationMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/BinaryNotificationMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/BinaryNotificationMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/DescriptionMessage : space/kscience/controls/api/DeviceMessage { + public static final field Companion Lspace/kscience/controls/api/DescriptionMessage$Companion; + public synthetic fun (ILspace/kscience/dataforge/meta/Meta;Ljava/util/Collection;Ljava/util/Collection;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Lspace/kscience/dataforge/meta/Meta;Ljava/util/Collection;Ljava/util/Collection;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V + public synthetic fun (Lspace/kscience/dataforge/meta/Meta;Ljava/util/Collection;Ljava/util/Collection;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun changeSource (Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/api/DeviceMessage; + public final fun component1 ()Lspace/kscience/dataforge/meta/Meta; + public final fun component2 ()Ljava/util/Collection; + public final fun component3 ()Ljava/util/Collection; + public final fun component4 ()Lspace/kscience/dataforge/names/Name; + public final fun component5 ()Lspace/kscience/dataforge/names/Name; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Lkotlinx/datetime/Instant; + public final fun copy (Lspace/kscience/dataforge/meta/Meta;Ljava/util/Collection;Ljava/util/Collection;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)Lspace/kscience/controls/api/DescriptionMessage; + public static synthetic fun copy$default (Lspace/kscience/controls/api/DescriptionMessage;Lspace/kscience/dataforge/meta/Meta;Ljava/util/Collection;Ljava/util/Collection;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILjava/lang/Object;)Lspace/kscience/controls/api/DescriptionMessage; + public fun equals (Ljava/lang/Object;)Z + public final fun getActions ()Ljava/util/Collection; + public fun getComment ()Ljava/lang/String; + public final fun getDescription ()Lspace/kscience/dataforge/meta/Meta; + public final fun getProperties ()Ljava/util/Collection; + public fun getSourceDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTargetDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTime ()Lkotlinx/datetime/Instant; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/api/DescriptionMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/DescriptionMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/DescriptionMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/DescriptionMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/DescriptionMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/DescriptionMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/DescriptorsKt { + public static final fun metaDescriptor (Lspace/kscience/controls/api/PropertyDescriptor;Lkotlin/jvm/functions/Function1;)V +} + +public abstract interface class space/kscience/controls/api/Device : java/lang/AutoCloseable, kotlinx/coroutines/CoroutineScope, space/kscience/dataforge/context/ContextAware { + public static final field Companion Lspace/kscience/controls/api/Device$Companion; + public static final field DEVICE_TARGET Ljava/lang/String; + public fun close ()V + public abstract fun execute (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun execute$default (Lspace/kscience/controls/api/Device;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public abstract fun getActionDescriptors ()Ljava/util/Collection; + public abstract fun getLifecycleState ()Lspace/kscience/controls/api/DeviceLifecycleState; + public abstract fun getMessageFlow ()Lkotlinx/coroutines/flow/Flow; + public fun getMeta ()Lspace/kscience/dataforge/meta/Meta; + public abstract fun getProperty (Ljava/lang/String;)Lspace/kscience/dataforge/meta/Meta; + public abstract fun getPropertyDescriptors ()Ljava/util/Collection; + public abstract fun invalidate (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun open (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun open$suspendImpl (Lspace/kscience/controls/api/Device;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun readProperty (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun writeProperty (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/api/Device$Companion { + public static final field DEVICE_TARGET Ljava/lang/String; +} + +public final class space/kscience/controls/api/DeviceErrorMessage : space/kscience/controls/api/DeviceMessage { + public static final field Companion Lspace/kscience/controls/api/DeviceErrorMessage$Companion; + public synthetic fun (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun changeSource (Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/api/DeviceMessage; + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Lspace/kscience/dataforge/names/Name; + public final fun component5 ()Lspace/kscience/dataforge/names/Name; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Lkotlinx/datetime/Instant; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)Lspace/kscience/controls/api/DeviceErrorMessage; + public static synthetic fun copy$default (Lspace/kscience/controls/api/DeviceErrorMessage;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILjava/lang/Object;)Lspace/kscience/controls/api/DeviceErrorMessage; + public fun equals (Ljava/lang/Object;)Z + public fun getComment ()Ljava/lang/String; + public final fun getErrorMessage ()Ljava/lang/String; + public final fun getErrorStackTrace ()Ljava/lang/String; + public final fun getErrorType ()Ljava/lang/String; + public fun getSourceDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTargetDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTime ()Lkotlinx/datetime/Instant; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/api/DeviceErrorMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/DeviceErrorMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/DeviceErrorMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/DeviceErrorMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/DeviceErrorMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/DeviceErrorMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public abstract interface class space/kscience/controls/api/DeviceHub : space/kscience/dataforge/provider/Provider { + public static final field Companion Lspace/kscience/controls/api/DeviceHub$Companion; + public fun content (Ljava/lang/String;)Ljava/util/Map; + public fun getDefaultChainTarget ()Ljava/lang/String; + public fun getDefaultTarget ()Ljava/lang/String; + public abstract fun getDevices ()Ljava/util/Map; +} + +public final class space/kscience/controls/api/DeviceHub$Companion { +} + +public final class space/kscience/controls/api/DeviceHubKt { + public static final fun execute (Lspace/kscience/controls/api/DeviceHub;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun get (Lspace/kscience/controls/api/DeviceHub;Ljava/lang/String;)Lspace/kscience/controls/api/Device; + public static final fun get (Lspace/kscience/controls/api/DeviceHub;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/controls/api/Device; + public static final fun get (Lspace/kscience/controls/api/DeviceHub;Lspace/kscience/dataforge/names/NameToken;)Lspace/kscience/controls/api/Device; + public static final fun getOrNull (Lspace/kscience/controls/api/DeviceHub;Ljava/lang/String;)Lspace/kscience/controls/api/Device; + public static final fun getOrNull (Lspace/kscience/controls/api/DeviceHub;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/controls/api/Device; + public static final fun readProperty (Lspace/kscience/controls/api/DeviceHub;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun writeProperty (Lspace/kscience/controls/api/DeviceHub;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/api/DeviceKt { + public static final fun getAllProperties (Lspace/kscience/controls/api/Device;)Lspace/kscience/dataforge/meta/Meta; + public static final fun getOrReadProperty (Lspace/kscience/controls/api/Device;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun onPropertyChange (Lspace/kscience/controls/api/Device;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job; +} + +public final class space/kscience/controls/api/DeviceLifecycleState : java/lang/Enum { + public static final field CLOSED Lspace/kscience/controls/api/DeviceLifecycleState; + public static final field INIT Lspace/kscience/controls/api/DeviceLifecycleState; + public static final field OPEN Lspace/kscience/controls/api/DeviceLifecycleState; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lspace/kscience/controls/api/DeviceLifecycleState; + public static fun values ()[Lspace/kscience/controls/api/DeviceLifecycleState; +} + +public final class space/kscience/controls/api/DeviceLogMessage : space/kscience/controls/api/DeviceMessage { + public static final field Companion Lspace/kscience/controls/api/DeviceLogMessage$Companion; + public synthetic fun (ILjava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V + public synthetic fun (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun changeSource (Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/api/DeviceMessage; + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lspace/kscience/dataforge/meta/Meta; + public final fun component3 ()Lspace/kscience/dataforge/names/Name; + public final fun component4 ()Lspace/kscience/dataforge/names/Name; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Lkotlinx/datetime/Instant; + public final fun copy (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)Lspace/kscience/controls/api/DeviceLogMessage; + public static synthetic fun copy$default (Lspace/kscience/controls/api/DeviceLogMessage;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILjava/lang/Object;)Lspace/kscience/controls/api/DeviceLogMessage; + public fun equals (Ljava/lang/Object;)Z + public fun getComment ()Ljava/lang/String; + public final fun getData ()Lspace/kscience/dataforge/meta/Meta; + public final fun getMessage ()Ljava/lang/String; + public fun getSourceDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTargetDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTime ()Lkotlinx/datetime/Instant; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/api/DeviceLogMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/DeviceLogMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/DeviceLogMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/DeviceLogMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/DeviceLogMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/DeviceLogMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public abstract class space/kscience/controls/api/DeviceMessage { + public static final field Companion Lspace/kscience/controls/api/DeviceMessage$Companion; + public synthetic fun (ILkotlinx/serialization/internal/SerializationConstructorMarker;)V + public abstract fun changeSource (Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/api/DeviceMessage; + public abstract fun getComment ()Ljava/lang/String; + public abstract fun getSourceDevice ()Lspace/kscience/dataforge/names/Name; + public abstract fun getTargetDevice ()Lspace/kscience/dataforge/names/Name; + public abstract fun getTime ()Lkotlinx/datetime/Instant; + public static final synthetic fun write$Self (Lspace/kscience/controls/api/DeviceMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/DeviceMessage$Companion { + public final fun error (Ljava/lang/Throwable;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/controls/api/DeviceErrorMessage; + public static synthetic fun error$default (Lspace/kscience/controls/api/DeviceMessage$Companion;Ljava/lang/Throwable;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;ILjava/lang/Object;)Lspace/kscience/controls/api/DeviceErrorMessage; + public final fun fromMeta (Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/api/DeviceMessage; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/DeviceMessageKt { + public static final fun toEnvelope (Lspace/kscience/controls/api/DeviceMessage;)Lspace/kscience/dataforge/io/Envelope; + public static final fun toMeta (Lspace/kscience/controls/api/DeviceMessage;)Lspace/kscience/dataforge/meta/Meta; +} + +public final class space/kscience/controls/api/EmptyDeviceMessage : space/kscience/controls/api/DeviceMessage { + public static final field Companion Lspace/kscience/controls/api/EmptyDeviceMessage$Companion; + public fun ()V + public synthetic fun (ILspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V + public synthetic fun (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun changeSource (Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/api/DeviceMessage; + public final fun component1 ()Lspace/kscience/dataforge/names/Name; + public final fun component2 ()Lspace/kscience/dataforge/names/Name; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Lkotlinx/datetime/Instant; + public final fun copy (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)Lspace/kscience/controls/api/EmptyDeviceMessage; + public static synthetic fun copy$default (Lspace/kscience/controls/api/EmptyDeviceMessage;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILjava/lang/Object;)Lspace/kscience/controls/api/EmptyDeviceMessage; + public fun equals (Ljava/lang/Object;)Z + public fun getComment ()Ljava/lang/String; + public fun getSourceDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTargetDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTime ()Lkotlinx/datetime/Instant; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/api/EmptyDeviceMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/EmptyDeviceMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/EmptyDeviceMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/EmptyDeviceMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/EmptyDeviceMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/EmptyDeviceMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/GetDescriptionMessage : space/kscience/controls/api/DeviceMessage { + public static final field Companion Lspace/kscience/controls/api/GetDescriptionMessage$Companion; + public synthetic fun (ILspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V + public synthetic fun (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun changeSource (Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/api/DeviceMessage; + public final fun component1 ()Lspace/kscience/dataforge/names/Name; + public final fun component2 ()Lspace/kscience/dataforge/names/Name; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Lkotlinx/datetime/Instant; + public final fun copy (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)Lspace/kscience/controls/api/GetDescriptionMessage; + public static synthetic fun copy$default (Lspace/kscience/controls/api/GetDescriptionMessage;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILjava/lang/Object;)Lspace/kscience/controls/api/GetDescriptionMessage; + public fun equals (Ljava/lang/Object;)Z + public fun getComment ()Ljava/lang/String; + public fun getSourceDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTargetDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTime ()Lkotlinx/datetime/Instant; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/api/GetDescriptionMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/GetDescriptionMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/GetDescriptionMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/GetDescriptionMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/GetDescriptionMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/GetDescriptionMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/PropertyChangedMessage : space/kscience/controls/api/DeviceMessage { + public static final field Companion Lspace/kscience/controls/api/PropertyChangedMessage$Companion; + public synthetic fun (ILjava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V + public synthetic fun (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun changeSource (Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/api/DeviceMessage; + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lspace/kscience/dataforge/meta/Meta; + public final fun component3 ()Lspace/kscience/dataforge/names/Name; + public final fun component4 ()Lspace/kscience/dataforge/names/Name; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Lkotlinx/datetime/Instant; + public final fun copy (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)Lspace/kscience/controls/api/PropertyChangedMessage; + public static synthetic fun copy$default (Lspace/kscience/controls/api/PropertyChangedMessage;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILjava/lang/Object;)Lspace/kscience/controls/api/PropertyChangedMessage; + public fun equals (Ljava/lang/Object;)Z + public fun getComment ()Ljava/lang/String; + public final fun getProperty ()Ljava/lang/String; + public fun getSourceDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTargetDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTime ()Lkotlinx/datetime/Instant; + public final fun getValue ()Lspace/kscience/dataforge/meta/Meta; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/api/PropertyChangedMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/PropertyChangedMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/PropertyChangedMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/PropertyChangedMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/PropertyChangedMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/PropertyChangedMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/PropertyDescriptor { + public static final field Companion Lspace/kscience/controls/api/PropertyDescriptor$Companion; + public synthetic fun (ILjava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ZZLkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ZZ)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getInfo ()Ljava/lang/String; + public final fun getMetaDescriptor ()Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor; + public final fun getName ()Ljava/lang/String; + public final fun getReadable ()Z + public final fun getWritable ()Z + public final fun setInfo (Ljava/lang/String;)V + public final fun setMetaDescriptor (Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;)V + public final fun setReadable (Z)V + public final fun setWritable (Z)V + public static final synthetic fun write$Self (Lspace/kscience/controls/api/PropertyDescriptor;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/PropertyDescriptor$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/PropertyDescriptor$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/PropertyDescriptor; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/PropertyDescriptor;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/PropertyDescriptor$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/PropertyGetMessage : space/kscience/controls/api/DeviceMessage { + public static final field Companion Lspace/kscience/controls/api/PropertyGetMessage$Companion; + public synthetic fun (ILjava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V + public synthetic fun (Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun changeSource (Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/api/DeviceMessage; + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lspace/kscience/dataforge/names/Name; + public final fun component3 ()Lspace/kscience/dataforge/names/Name; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Lkotlinx/datetime/Instant; + public final fun copy (Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)Lspace/kscience/controls/api/PropertyGetMessage; + public static synthetic fun copy$default (Lspace/kscience/controls/api/PropertyGetMessage;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILjava/lang/Object;)Lspace/kscience/controls/api/PropertyGetMessage; + public fun equals (Ljava/lang/Object;)Z + public fun getComment ()Ljava/lang/String; + public final fun getProperty ()Ljava/lang/String; + public fun getSourceDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTargetDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTime ()Lkotlinx/datetime/Instant; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/api/PropertyGetMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/PropertyGetMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/PropertyGetMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/PropertyGetMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/PropertyGetMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/PropertyGetMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/PropertySetMessage : space/kscience/controls/api/DeviceMessage { + public static final field Companion Lspace/kscience/controls/api/PropertySetMessage$Companion; + public synthetic fun (ILjava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V + public synthetic fun (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun changeSource (Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/api/DeviceMessage; + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lspace/kscience/dataforge/meta/Meta; + public final fun component3 ()Lspace/kscience/dataforge/names/Name; + public final fun component4 ()Lspace/kscience/dataforge/names/Name; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Lkotlinx/datetime/Instant; + public final fun copy (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)Lspace/kscience/controls/api/PropertySetMessage; + public static synthetic fun copy$default (Lspace/kscience/controls/api/PropertySetMessage;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;ILjava/lang/Object;)Lspace/kscience/controls/api/PropertySetMessage; + public fun equals (Ljava/lang/Object;)Z + public fun getComment ()Ljava/lang/String; + public final fun getProperty ()Ljava/lang/String; + public fun getSourceDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTargetDevice ()Lspace/kscience/dataforge/names/Name; + public fun getTime ()Lkotlinx/datetime/Instant; + public final fun getValue ()Lspace/kscience/dataforge/meta/Meta; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/api/PropertySetMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/api/PropertySetMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/api/PropertySetMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/api/PropertySetMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/api/PropertySetMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/api/PropertySetMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public abstract interface class space/kscience/controls/api/Socket : java/io/Closeable { + public abstract fun isOpen ()Z + public abstract fun receiving ()Lkotlinx/coroutines/flow/Flow; + public abstract fun send (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/api/SocketKt { + public static final fun connectInput (Lspace/kscience/controls/api/Socket;Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/Job; +} + +public final class space/kscience/controls/manager/DeviceManager : space/kscience/dataforge/context/AbstractPlugin, space/kscience/controls/api/DeviceHub { + public static final field Companion Lspace/kscience/controls/manager/DeviceManager$Companion; + public fun ()V + public fun content (Ljava/lang/String;)Ljava/util/Map; + public fun getDevices ()Ljava/util/Map; + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; + public final fun registerDevice (Lspace/kscience/dataforge/names/NameToken;Lspace/kscience/controls/api/Device;)V +} + +public final class space/kscience/controls/manager/DeviceManager$Companion : space/kscience/dataforge/context/PluginFactory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/manager/DeviceManager; + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; +} + +public final class space/kscience/controls/manager/DeviceManagerKt { + public static final fun install (Lspace/kscience/controls/manager/DeviceManager;Ljava/lang/String;Lspace/kscience/controls/api/Device;)Lspace/kscience/controls/api/Device; + public static final fun install (Lspace/kscience/controls/manager/DeviceManager;Ljava/lang/String;Lspace/kscience/dataforge/context/Factory;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/api/Device; + public static synthetic fun install$default (Lspace/kscience/controls/manager/DeviceManager;Ljava/lang/String;Lspace/kscience/dataforge/context/Factory;Lspace/kscience/dataforge/meta/Meta;ILjava/lang/Object;)Lspace/kscience/controls/api/Device; + public static final fun installing (Lspace/kscience/controls/manager/DeviceManager;Lspace/kscience/dataforge/context/Factory;Lkotlin/jvm/functions/Function1;)Lkotlin/properties/ReadOnlyProperty; + public static synthetic fun installing$default (Lspace/kscience/controls/manager/DeviceManager;Lspace/kscience/dataforge/context/Factory;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/properties/ReadOnlyProperty; +} + +public final class space/kscience/controls/manager/RespondMessageKt { + public static final fun hubMessageFlow (Lspace/kscience/controls/api/DeviceHub;Lkotlinx/coroutines/CoroutineScope;)Lkotlinx/coroutines/flow/Flow; + public static final fun respondHubMessage (Lspace/kscience/controls/api/DeviceHub;Lspace/kscience/controls/api/DeviceMessage;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun respondMessage (Lspace/kscience/controls/api/Device;Lspace/kscience/dataforge/names/Name;Lspace/kscience/controls/api/DeviceMessage;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/misc/TimeMetaKt { + public static final fun instant (Lspace/kscience/dataforge/meta/Meta;)Lkotlinx/datetime/Instant; + public static final fun toMeta (Lkotlinx/datetime/Instant;)Lspace/kscience/dataforge/meta/Meta; +} + +public abstract class space/kscience/controls/ports/AbstractPort : space/kscience/controls/ports/Port { + public fun (Lspace/kscience/dataforge/context/Context;Lkotlin/coroutines/CoroutineContext;)V + public synthetic fun (Lspace/kscience/dataforge/context/Context;Lkotlin/coroutines/CoroutineContext;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun close ()V + public fun getContext ()Lspace/kscience/dataforge/context/Context; + protected final fun getScope ()Lkotlinx/coroutines/CoroutineScope; + public fun isOpen ()Z + protected final fun receive ([BLkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun receiving ()Lkotlinx/coroutines/flow/Flow; + public synthetic fun send (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun send ([BLkotlin/coroutines/Continuation;)Ljava/lang/Object; + protected abstract fun write ([BLkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/ports/ChannelPort : space/kscience/controls/ports/AbstractPort, java/lang/AutoCloseable { + public fun (Lspace/kscience/dataforge/context/Context;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function1;)V + public synthetic fun (Lspace/kscience/dataforge/context/Context;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun close ()V + public final fun getStartJob ()Lkotlinx/coroutines/Job; +} + +public final class space/kscience/controls/ports/ChannelPortKt { + public static final fun toArray (Ljava/nio/ByteBuffer;I)[B + public static synthetic fun toArray$default (Ljava/nio/ByteBuffer;IILjava/lang/Object;)[B +} + +public final class space/kscience/controls/ports/JvmPortsPlugin : space/kscience/dataforge/context/AbstractPlugin { + public static final field Companion Lspace/kscience/controls/ports/JvmPortsPlugin$Companion; + public fun ()V + public fun content (Ljava/lang/String;)Ljava/util/Map; + public final fun getPorts ()Lspace/kscience/controls/ports/Ports; + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; +} + +public final class space/kscience/controls/ports/JvmPortsPlugin$Companion : space/kscience/dataforge/context/PluginFactory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/ports/JvmPortsPlugin; + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; +} + +public final class space/kscience/controls/ports/PhrasesKt { + public static final fun delimitedIncoming (Lspace/kscience/controls/ports/Port;[B)Lkotlinx/coroutines/flow/Flow; + public static final fun stringsDelimitedIncoming (Lspace/kscience/controls/ports/Port;Ljava/lang/String;)Lkotlinx/coroutines/flow/Flow; + public static final fun withDelimiter (Lkotlinx/coroutines/flow/Flow;[B)Lkotlinx/coroutines/flow/Flow; + public static final fun withStringDelimiter (Lkotlinx/coroutines/flow/Flow;Ljava/lang/String;)Lkotlinx/coroutines/flow/Flow; +} + +public abstract interface class space/kscience/controls/ports/Port : space/kscience/controls/api/Socket, space/kscience/dataforge/context/ContextAware { +} + +public abstract interface class space/kscience/controls/ports/PortFactory : space/kscience/dataforge/context/Factory { + public static final field Companion Lspace/kscience/controls/ports/PortFactory$Companion; + public static final field TYPE Ljava/lang/String; + public abstract fun getType ()Ljava/lang/String; +} + +public final class space/kscience/controls/ports/PortFactory$Companion { + public static final field TYPE Ljava/lang/String; +} + +public final class space/kscience/controls/ports/PortKt { + public static final fun send (Lspace/kscience/controls/ports/Port;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/ports/PortProxy : space/kscience/controls/ports/Port, space/kscience/dataforge/context/ContextAware { + public fun (Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;)V + public synthetic fun (Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun close ()V + public fun getContext ()Lspace/kscience/dataforge/context/Context; + public final fun getFactory ()Lkotlin/jvm/functions/Function1; + public fun isOpen ()Z + public fun receiving ()Lkotlinx/coroutines/flow/Flow; + public synthetic fun send (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun send ([BLkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/ports/Ports : space/kscience/dataforge/context/AbstractPlugin { + public static final field Companion Lspace/kscience/controls/ports/Ports$Companion; + public fun ()V + public final fun buildPort (Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/ports/Port; + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; +} + +public final class space/kscience/controls/ports/Ports$Companion : space/kscience/dataforge/context/PluginFactory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/ports/Ports; + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; +} + +public final class space/kscience/controls/ports/SynchronousPort : space/kscience/controls/ports/Port { + public fun (Lspace/kscience/controls/ports/Port;Lkotlinx/coroutines/sync/Mutex;)V + public fun close ()V + public fun getContext ()Lspace/kscience/dataforge/context/Context; + public final fun getPort ()Lspace/kscience/controls/ports/Port; + public fun isOpen ()Z + public fun receiving ()Lkotlinx/coroutines/flow/Flow; + public final fun respond ([BLkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public synthetic fun send (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun send ([BLkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/ports/SynchronousPortKt { + public static final fun respondStringWithDelimiter (Lspace/kscience/controls/ports/SynchronousPort;Ljava/lang/String;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun respondWithDelimiter (Lspace/kscience/controls/ports/SynchronousPort;[B[BLkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun synchronous (Lspace/kscience/controls/ports/Port;Lkotlinx/coroutines/sync/Mutex;)Lspace/kscience/controls/ports/SynchronousPort; + public static synthetic fun synchronous$default (Lspace/kscience/controls/ports/Port;Lkotlinx/coroutines/sync/Mutex;ILjava/lang/Object;)Lspace/kscience/controls/ports/SynchronousPort; +} + +public final class space/kscience/controls/ports/TcpPort : space/kscience/controls/ports/PortFactory { + public static final field INSTANCE Lspace/kscience/controls/ports/TcpPort; + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/ports/ChannelPort; + public fun getType ()Ljava/lang/String; + public final fun open (Lspace/kscience/dataforge/context/Context;Ljava/lang/String;ILkotlin/coroutines/CoroutineContext;)Lspace/kscience/controls/ports/ChannelPort; + public static synthetic fun open$default (Lspace/kscience/controls/ports/TcpPort;Lspace/kscience/dataforge/context/Context;Ljava/lang/String;ILkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lspace/kscience/controls/ports/ChannelPort; +} + +public final class space/kscience/controls/ports/UdpPort : space/kscience/controls/ports/PortFactory { + public static final field INSTANCE Lspace/kscience/controls/ports/UdpPort; + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/ports/ChannelPort; + public fun getType ()Ljava/lang/String; + public final fun open (Lspace/kscience/dataforge/context/Context;Ljava/lang/String;ILjava/lang/Integer;Ljava/lang/String;Lkotlin/coroutines/CoroutineContext;)Lspace/kscience/controls/ports/ChannelPort; + public static synthetic fun open$default (Lspace/kscience/controls/ports/UdpPort;Lspace/kscience/dataforge/context/Context;Ljava/lang/String;ILjava/lang/Integer;Ljava/lang/String;Lkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lspace/kscience/controls/ports/ChannelPort; +} + +public abstract interface class space/kscience/controls/spec/DeviceActionSpec { + public abstract fun execute (Lspace/kscience/controls/api/Device;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun getDescriptor ()Lspace/kscience/controls/api/ActionDescriptor; + public abstract fun getInputConverter ()Lspace/kscience/dataforge/meta/transformations/MetaConverter; + public abstract fun getOutputConverter ()Lspace/kscience/dataforge/meta/transformations/MetaConverter; +} + +public abstract class space/kscience/controls/spec/DeviceBase : space/kscience/controls/api/Device { + public fun (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)V + public synthetic fun (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun close ()V + public fun execute (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun getActionDescriptors ()Ljava/util/Collection; + public abstract fun getActions ()Ljava/util/Map; + public final fun getContext ()Lspace/kscience/dataforge/context/Context; + public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; + public fun getLifecycleState ()Lspace/kscience/controls/api/DeviceLifecycleState; + public synthetic fun getMessageFlow ()Lkotlinx/coroutines/flow/Flow; + public fun getMessageFlow ()Lkotlinx/coroutines/flow/SharedFlow; + public fun getMeta ()Lspace/kscience/dataforge/meta/Meta; + public abstract fun getProperties ()Ljava/util/Map; + public fun getProperty (Ljava/lang/String;)Lspace/kscience/dataforge/meta/Meta; + public fun getPropertyDescriptors ()Ljava/util/Collection; + public fun invalidate (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun open (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun readProperty (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun readPropertyOrNull (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + protected fun setLifecycleState (Lspace/kscience/controls/api/DeviceLifecycleState;)V + public abstract fun toString ()Ljava/lang/String; + protected final fun updateLogical (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun updateLogical (Lspace/kscience/controls/spec/DevicePropertySpec;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun writeProperty (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public class space/kscience/controls/spec/DeviceBySpec : space/kscience/controls/spec/DeviceBase { + public fun (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)V + public synthetic fun (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun close ()V + public fun getActions ()Ljava/util/Map; + public fun getProperties ()Ljava/util/Map; + public final fun getSpec ()Lspace/kscience/controls/spec/DeviceSpec; + public fun open (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun toString ()Ljava/lang/String; +} + +public final class space/kscience/controls/spec/DeviceExtensionsKt { + public static final fun doRecurring-8Mi8wO0 (Lspace/kscience/controls/api/Device;JLkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job; + public static final fun readRecurring-8Mi8wO0 (Lspace/kscience/controls/api/Device;JLkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/flow/Flow; +} + +public abstract interface class space/kscience/controls/spec/DevicePropertySpec { + public abstract fun getConverter ()Lspace/kscience/dataforge/meta/transformations/MetaConverter; + public abstract fun getDescriptor ()Lspace/kscience/controls/api/PropertyDescriptor; + public abstract fun read (Lspace/kscience/controls/api/Device;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/spec/DevicePropertySpecKt { + public static final fun execute (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/spec/DeviceActionSpec;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun execute (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/spec/DeviceActionSpec;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun get (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/spec/DevicePropertySpec;)Ljava/lang/Object; + public static final fun getName (Lspace/kscience/controls/spec/DeviceActionSpec;)Ljava/lang/String; + public static final fun getName (Lspace/kscience/controls/spec/DevicePropertySpec;)Ljava/lang/String; + public static final fun invalidate (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/spec/DevicePropertySpec;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun onPropertyChange (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/spec/DevicePropertySpec;Lkotlin/jvm/functions/Function3;)Lkotlinx/coroutines/Job; + public static final fun propertyFlow (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/spec/DevicePropertySpec;)Lkotlinx/coroutines/flow/Flow; + public static final fun read (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/spec/DevicePropertySpec;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun readOrNull (Lspace/kscience/controls/spec/DeviceBase;Lspace/kscience/controls/spec/DevicePropertySpec;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun set (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/spec/WritableDevicePropertySpec;Ljava/lang/Object;)Lkotlinx/coroutines/Job; + public static final fun useProperty (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/spec/DevicePropertySpec;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job; + public static final fun write (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/spec/WritableDevicePropertySpec;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract class space/kscience/controls/spec/DeviceSpec { + public fun ()V + public final fun action (Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function3;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun action$default (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public final fun getActions ()Ljava/util/Map; + public final fun getProperties ()Ljava/util/Map; + public final fun metaAction (Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function3;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun metaAction$default (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public final fun mutableProperty (Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;)Lkotlin/properties/PropertyDelegateProvider; + public final fun mutableProperty (Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lkotlin/reflect/KMutableProperty1;Lkotlin/jvm/functions/Function1;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun mutableProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun mutableProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lkotlin/reflect/KMutableProperty1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public fun onClose (Lspace/kscience/controls/api/Device;)V + public fun onOpen (Lspace/kscience/controls/api/Device;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun property (Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Lkotlin/properties/PropertyDelegateProvider; + public final fun property (Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lkotlin/reflect/KProperty1;Lkotlin/jvm/functions/Function1;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun property$default (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun property$default (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lkotlin/reflect/KProperty1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public final fun registerAction (Lspace/kscience/controls/spec/DeviceActionSpec;)Lspace/kscience/controls/spec/DeviceActionSpec; + public final fun registerProperty (Lspace/kscience/controls/spec/DevicePropertySpec;)Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun unitAction (Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun unitAction$default (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; +} + +public final class space/kscience/controls/spec/DeviceSpecKt { + public static final fun getUnit (Lspace/kscience/dataforge/meta/transformations/MetaConverter$Companion;)Lspace/kscience/dataforge/meta/transformations/MetaConverter; + public static final fun logicalProperty (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lkotlin/jvm/functions/Function1;Ljava/lang/String;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun logicalProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/dataforge/meta/transformations/MetaConverter;Lkotlin/jvm/functions/Function1;Ljava/lang/String;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; +} + +public final class space/kscience/controls/spec/DurationConverter : space/kscience/dataforge/meta/transformations/MetaConverter { + public static final field INSTANCE Lspace/kscience/controls/spec/DurationConverter; + public synthetic fun metaToObject (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun metaToObject-5sfh64U (Lspace/kscience/dataforge/meta/Meta;)J + public synthetic fun objectToMeta (Ljava/lang/Object;)Lspace/kscience/dataforge/meta/Meta; + public fun objectToMeta-LRDsOJo (J)Lspace/kscience/dataforge/meta/Meta; +} + +public abstract interface annotation class space/kscience/controls/spec/InternalDeviceAPI : java/lang/annotation/Annotation { +} + +public final class space/kscience/controls/spec/MiscKt { + public static final fun asMeta (D)Lspace/kscience/dataforge/meta/Meta; + public static final fun getDuration (Lspace/kscience/dataforge/meta/transformations/MetaConverter$Companion;)Lspace/kscience/dataforge/meta/transformations/MetaConverter; +} + +public final class space/kscience/controls/spec/PropertySpecDelegatesKt { + public static final fun booleanProperty (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Lkotlin/properties/PropertyDelegateProvider; + public static final fun booleanProperty (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun booleanProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun booleanProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public static final fun doubleProperty (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Lkotlin/properties/PropertyDelegateProvider; + public static final fun doubleProperty (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun doubleProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun doubleProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public static final fun metaProperty (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Lkotlin/properties/PropertyDelegateProvider; + public static final fun metaProperty (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun metaProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun metaProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public static final fun numberProperty (Lspace/kscience/controls/spec/DeviceSpec;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Lkotlin/properties/PropertyDelegateProvider; + public static final fun numberProperty (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun numberProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun numberProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public static final fun stringProperty (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Lkotlin/properties/PropertyDelegateProvider; + public static final fun stringProperty (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun stringProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; + public static synthetic fun stringProperty$default (Lspace/kscience/controls/spec/DeviceSpec;Lkotlin/jvm/functions/Function1;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;ILjava/lang/Object;)Lkotlin/properties/PropertyDelegateProvider; +} + +public final class space/kscience/controls/spec/UnitMetaConverter : space/kscience/dataforge/meta/transformations/MetaConverter { + public static final field INSTANCE Lspace/kscience/controls/spec/UnitMetaConverter; + public synthetic fun metaToObject (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun metaToObject (Lspace/kscience/dataforge/meta/Meta;)V + public synthetic fun objectToMeta (Ljava/lang/Object;)Lspace/kscience/dataforge/meta/Meta; + public fun objectToMeta (Lkotlin/Unit;)Lspace/kscience/dataforge/meta/Meta; +} + +public abstract interface class space/kscience/controls/spec/WritableDevicePropertySpec : space/kscience/controls/spec/DevicePropertySpec { + public abstract fun write (Lspace/kscience/controls/api/Device;Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + diff --git a/controls-magix/api/controls-magix.api b/controls-magix/api/controls-magix.api new file mode 100644 index 0000000..b892b3f --- /dev/null +++ b/controls-magix/api/controls-magix.api @@ -0,0 +1,199 @@ +public final class space/kscience/controls/client/ControlsMagixKt { + public static final fun getMagixFormat (Lspace/kscience/controls/manager/DeviceManager$Companion;)Lspace/kscience/magix/api/MagixFormat; + public static final fun launchMagixService (Lspace/kscience/controls/manager/DeviceManager;Lspace/kscience/magix/api/MagixEndpoint;Ljava/lang/String;)Lkotlinx/coroutines/Job; + public static synthetic fun launchMagixService$default (Lspace/kscience/controls/manager/DeviceManager;Lspace/kscience/magix/api/MagixEndpoint;Ljava/lang/String;ILjava/lang/Object;)Lkotlinx/coroutines/Job; +} + +public final class space/kscience/controls/client/DeviceClient : space/kscience/controls/api/Device { + public fun (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/names/Name;Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;)V + public fun execute (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun getActionDescriptors ()Ljava/util/Collection; + public fun getContext ()Lspace/kscience/dataforge/context/Context; + public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; + public fun getLifecycleState ()Lspace/kscience/controls/api/DeviceLifecycleState; + public fun getMessageFlow ()Lkotlinx/coroutines/flow/Flow; + public fun getProperty (Ljava/lang/String;)Lspace/kscience/dataforge/meta/Meta; + public fun getPropertyDescriptors ()Ljava/util/Collection; + public fun invalidate (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun readProperty (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun writeProperty (Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/client/DeviceClientKt { + public static final fun remoteDevice (Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/dataforge/context/Context;Ljava/lang/String;Lspace/kscience/dataforge/names/Name;)Lspace/kscience/controls/client/DeviceClient; +} + +public final class space/kscience/controls/client/DoocsAction : java/lang/Enum { + public static final field Companion Lspace/kscience/controls/client/DoocsAction$Companion; + public static final field get Lspace/kscience/controls/client/DoocsAction; + public static final field names Lspace/kscience/controls/client/DoocsAction; + public static final field set Lspace/kscience/controls/client/DoocsAction; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lspace/kscience/controls/client/DoocsAction; + public static fun values ()[Lspace/kscience/controls/client/DoocsAction; +} + +public final class space/kscience/controls/client/DoocsAction$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/client/DoocsPayload { + public static final field Companion Lspace/kscience/controls/client/DoocsPayload$Companion; + public synthetic fun (ILspace/kscience/controls/client/DoocsAction;Ljava/lang/String;Lspace/kscience/controls/client/EqData;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Lspace/kscience/controls/client/DoocsAction;Ljava/lang/String;Lspace/kscience/controls/client/EqData;)V + public final fun component1 ()Lspace/kscience/controls/client/DoocsAction; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Lspace/kscience/controls/client/EqData; + public final fun copy (Lspace/kscience/controls/client/DoocsAction;Ljava/lang/String;Lspace/kscience/controls/client/EqData;)Lspace/kscience/controls/client/DoocsPayload; + public static synthetic fun copy$default (Lspace/kscience/controls/client/DoocsPayload;Lspace/kscience/controls/client/DoocsAction;Ljava/lang/String;Lspace/kscience/controls/client/EqData;ILjava/lang/Object;)Lspace/kscience/controls/client/DoocsPayload; + public fun equals (Ljava/lang/Object;)Z + public final fun getAction ()Lspace/kscience/controls/client/DoocsAction; + public final fun getAddress ()Ljava/lang/String; + public final fun getData ()Lspace/kscience/controls/client/EqData; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/client/DoocsPayload;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/client/DoocsPayload$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/client/DoocsPayload$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/client/DoocsPayload; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/client/DoocsPayload;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/client/DoocsPayload$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/client/EqData { + public static final field Companion Lspace/kscience/controls/client/EqData$Companion; + public synthetic fun (IILjava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (ILjava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/String;)V + public synthetic fun (ILjava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()I + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Lspace/kscience/dataforge/meta/Meta; + public final fun component4 ()Ljava/lang/Integer; + public final fun component5 ()Ljava/lang/Integer; + public final fun component6 ()Ljava/lang/Long; + public final fun component7 ()Ljava/lang/String; + public final fun copy (ILjava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/String;)Lspace/kscience/controls/client/EqData; + public static synthetic fun copy$default (Lspace/kscience/controls/client/EqData;ILjava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/String;ILjava/lang/Object;)Lspace/kscience/controls/client/EqData; + public fun equals (Ljava/lang/Object;)Z + public final fun getError ()Ljava/lang/Integer; + public final fun getEventId ()Ljava/lang/Integer; + public final fun getMessage ()Ljava/lang/String; + public final fun getTime ()Ljava/lang/Long; + public final fun getType ()Ljava/lang/String; + public final fun getTypeId ()I + public final fun getValue ()Lspace/kscience/dataforge/meta/Meta; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/client/EqData;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/client/EqData$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/client/EqData$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/client/EqData; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/client/EqData;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/client/EqData$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/client/TangoAction : java/lang/Enum { + public static final field Companion Lspace/kscience/controls/client/TangoAction$Companion; + public static final field exec Lspace/kscience/controls/client/TangoAction; + public static final field pipe Lspace/kscience/controls/client/TangoAction; + public static final field read Lspace/kscience/controls/client/TangoAction; + public static final field write Lspace/kscience/controls/client/TangoAction; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lspace/kscience/controls/client/TangoAction; + public static fun values ()[Lspace/kscience/controls/client/TangoAction; +} + +public final class space/kscience/controls/client/TangoAction$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/client/TangoMagixKt { + public static final field TANGO_MAGIX_FORMAT Ljava/lang/String; + public static final fun launchTangoMagix (Lspace/kscience/controls/manager/DeviceManager;Lspace/kscience/magix/api/MagixEndpoint;Ljava/lang/String;)Lkotlinx/coroutines/Job; + public static synthetic fun launchTangoMagix$default (Lspace/kscience/controls/manager/DeviceManager;Lspace/kscience/magix/api/MagixEndpoint;Ljava/lang/String;ILjava/lang/Object;)Lkotlinx/coroutines/Job; +} + +public final class space/kscience/controls/client/TangoPayload { + public static final field Companion Lspace/kscience/controls/client/TangoPayload$Companion; + public synthetic fun (ILspace/kscience/controls/client/TangoAction;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/controls/client/TangoQuality;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;Ljava/util/List;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Lspace/kscience/controls/client/TangoAction;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/controls/client/TangoQuality;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;Ljava/util/List;)V + public synthetic fun (Lspace/kscience/controls/client/TangoAction;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/controls/client/TangoQuality;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lspace/kscience/controls/client/TangoAction; + public final fun component10 ()Lspace/kscience/dataforge/meta/Meta; + public final fun component11 ()Ljava/util/List; + public final fun component2 ()I + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Lspace/kscience/dataforge/meta/Meta; + public final fun component7 ()Lspace/kscience/controls/client/TangoQuality; + public final fun component8 ()Lspace/kscience/dataforge/meta/Meta; + public final fun component9 ()Lspace/kscience/dataforge/meta/Meta; + public final fun copy (Lspace/kscience/controls/client/TangoAction;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/controls/client/TangoQuality;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;Ljava/util/List;)Lspace/kscience/controls/client/TangoPayload; + public static synthetic fun copy$default (Lspace/kscience/controls/client/TangoPayload;Lspace/kscience/controls/client/TangoAction;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/controls/client/TangoQuality;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;Lspace/kscience/dataforge/meta/Meta;Ljava/util/List;ILjava/lang/Object;)Lspace/kscience/controls/client/TangoPayload; + public fun equals (Ljava/lang/Object;)Z + public final fun getAction ()Lspace/kscience/controls/client/TangoAction; + public final fun getArgin ()Lspace/kscience/dataforge/meta/Meta; + public final fun getArgout ()Lspace/kscience/dataforge/meta/Meta; + public final fun getData ()Lspace/kscience/dataforge/meta/Meta; + public final fun getDevice ()Ljava/lang/String; + public final fun getErrors ()Ljava/util/List; + public final fun getHost ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public final fun getQuality ()Lspace/kscience/controls/client/TangoQuality; + public final fun getTimestamp ()I + public final fun getValue ()Lspace/kscience/dataforge/meta/Meta; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/controls/client/TangoPayload;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/controls/client/TangoPayload$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/controls/client/TangoPayload$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/controls/client/TangoPayload; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/controls/client/TangoPayload;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/client/TangoPayload$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/controls/client/TangoQuality : java/lang/Enum { + public static final field ALARM Lspace/kscience/controls/client/TangoQuality; + public static final field Companion Lspace/kscience/controls/client/TangoQuality$Companion; + public static final field VALID Lspace/kscience/controls/client/TangoQuality; + public static final field WARNING Lspace/kscience/controls/client/TangoQuality; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lspace/kscience/controls/client/TangoQuality; + public static fun values ()[Lspace/kscience/controls/client/TangoQuality; +} + +public final class space/kscience/controls/client/TangoQuality$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + diff --git a/controls-modbus/api/controls-modbus.api b/controls-modbus/api/controls-modbus.api new file mode 100644 index 0000000..36ad071 --- /dev/null +++ b/controls-modbus/api/controls-modbus.api @@ -0,0 +1,166 @@ +public final class space/kscience/controls/modbus/DeviceProcessImageBuilder { + public final fun bind (Lspace/kscience/controls/modbus/ModbusRegistryKey$Coil;Lkotlin/jvm/functions/Function2;)Lcom/ghgande/j2mod/modbus/procimg/ObservableDigitalOut; + public final fun bind (Lspace/kscience/controls/modbus/ModbusRegistryKey$Coil;Lspace/kscience/controls/spec/WritableDevicePropertySpec;)Lcom/ghgande/j2mod/modbus/procimg/ObservableDigitalOut; + public final fun bind (Lspace/kscience/controls/modbus/ModbusRegistryKey$DiscreteInput;Lkotlin/jvm/functions/Function2;)Lcom/ghgande/j2mod/modbus/procimg/DigitalIn; + public final fun bind (Lspace/kscience/controls/modbus/ModbusRegistryKey$DiscreteInput;Lspace/kscience/controls/spec/DevicePropertySpec;)Lcom/ghgande/j2mod/modbus/procimg/DigitalIn; + public final fun bind (Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRange;Lspace/kscience/controls/spec/WritableDevicePropertySpec;)V + public final fun bind (Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRegister;Lkotlin/jvm/functions/Function2;)Lcom/ghgande/j2mod/modbus/procimg/ObservableRegister; + public final fun bind (Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRegister;Lspace/kscience/controls/spec/WritableDevicePropertySpec;)Lcom/ghgande/j2mod/modbus/procimg/ObservableRegister; + public final fun bind (Lspace/kscience/controls/modbus/ModbusRegistryKey$InputRange;Lspace/kscience/controls/spec/DevicePropertySpec;)V + public final fun bind (Lspace/kscience/controls/modbus/ModbusRegistryKey$InputRegister;Lkotlin/jvm/functions/Function2;)Lcom/ghgande/j2mod/modbus/procimg/SimpleInputRegister; + public final fun bind (Lspace/kscience/controls/modbus/ModbusRegistryKey$InputRegister;Lspace/kscience/controls/spec/DevicePropertySpec;)Lcom/ghgande/j2mod/modbus/procimg/SimpleInputRegister; + public static synthetic fun bind$default (Lspace/kscience/controls/modbus/DeviceProcessImageBuilder;Lspace/kscience/controls/modbus/ModbusRegistryKey$Coil;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/ghgande/j2mod/modbus/procimg/ObservableDigitalOut; + public static synthetic fun bind$default (Lspace/kscience/controls/modbus/DeviceProcessImageBuilder;Lspace/kscience/controls/modbus/ModbusRegistryKey$DiscreteInput;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/ghgande/j2mod/modbus/procimg/DigitalIn; + public static synthetic fun bind$default (Lspace/kscience/controls/modbus/DeviceProcessImageBuilder;Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRegister;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/ghgande/j2mod/modbus/procimg/ObservableRegister; + public static synthetic fun bind$default (Lspace/kscience/controls/modbus/DeviceProcessImageBuilder;Lspace/kscience/controls/modbus/ModbusRegistryKey$InputRegister;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lcom/ghgande/j2mod/modbus/procimg/SimpleInputRegister; + public final fun bindAction (Lspace/kscience/controls/modbus/ModbusRegistryKey$Coil;Lkotlin/jvm/functions/Function3;)Lcom/ghgande/j2mod/modbus/procimg/ObservableDigitalOut; + public final fun bindAction (Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRange;Lkotlin/jvm/functions/Function3;)Ljava/util/List; + public final fun bindAction (Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRegister;Lkotlin/jvm/functions/Function3;)Lcom/ghgande/j2mod/modbus/procimg/ObservableRegister; + public final fun getImage ()Lcom/ghgande/j2mod/modbus/procimg/ProcessImageImplementation; +} + +public final class space/kscience/controls/modbus/DeviceProcessImageKt { + public static final fun bindProcessImage (Lspace/kscience/controls/api/Device;ZLkotlin/jvm/functions/Function1;)Lcom/ghgande/j2mod/modbus/procimg/ProcessImage; + public static synthetic fun bindProcessImage$default (Lspace/kscience/controls/api/Device;ZLkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lcom/ghgande/j2mod/modbus/procimg/ProcessImage; +} + +public abstract interface class space/kscience/controls/modbus/ModbusDevice : space/kscience/controls/api/Device { + public abstract fun getClientId ()I + public abstract fun getMaster ()Lcom/ghgande/j2mod/modbus/facade/AbstractModbusMaster; + public fun getValue (Lspace/kscience/controls/modbus/ModbusRegistryKey$Coil;Ljava/lang/Object;Lkotlin/reflect/KProperty;)Z + public fun getValue (Lspace/kscience/controls/modbus/ModbusRegistryKey$DiscreteInput;Ljava/lang/Object;Lkotlin/reflect/KProperty;)Z + public fun getValue (Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRange;Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object; + public fun getValue (Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRegister;Ljava/lang/Object;Lkotlin/reflect/KProperty;)S + public fun getValue (Lspace/kscience/controls/modbus/ModbusRegistryKey$InputRange;Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object; + public fun getValue (Lspace/kscience/controls/modbus/ModbusRegistryKey$InputRegister;Ljava/lang/Object;Lkotlin/reflect/KProperty;)S + public fun setValue (Lspace/kscience/controls/modbus/ModbusRegistryKey$Coil;Ljava/lang/Object;Lkotlin/reflect/KProperty;Z)V + public fun setValue (Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRange;Ljava/lang/Object;Lkotlin/reflect/KProperty;Ljava/lang/Object;)V + public fun setValue (Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRegister;Ljava/lang/Object;Lkotlin/reflect/KProperty;S)V +} + +public class space/kscience/controls/modbus/ModbusDeviceBySpec : space/kscience/controls/spec/DeviceBySpec, space/kscience/controls/modbus/ModbusDevice { + public fun (Lspace/kscience/dataforge/context/Context;Lspace/kscience/controls/spec/DeviceSpec;ILcom/ghgande/j2mod/modbus/facade/AbstractModbusMaster;ZLspace/kscience/dataforge/meta/Meta;)V + public synthetic fun (Lspace/kscience/dataforge/context/Context;Lspace/kscience/controls/spec/DeviceSpec;ILcom/ghgande/j2mod/modbus/facade/AbstractModbusMaster;ZLspace/kscience/dataforge/meta/Meta;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun close ()V + public fun getClientId ()I + public fun getMaster ()Lcom/ghgande/j2mod/modbus/facade/AbstractModbusMaster; + public fun open (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/modbus/ModbusDeviceKt { + public static final fun modbusDoubleRegister (Lspace/kscience/controls/modbus/ModbusDevice;I)Lkotlin/properties/ReadWriteProperty; + public static final fun modbusRegister (Lspace/kscience/controls/modbus/ModbusDevice;I)Lkotlin/properties/ReadWriteProperty; + public static final fun readCoil (Lspace/kscience/controls/modbus/ModbusDevice;I)Z + public static final fun readCoils (Lspace/kscience/controls/modbus/ModbusDevice;II)Lcom/ghgande/j2mod/modbus/util/BitVector; + public static final fun readDoubleInput (Lspace/kscience/controls/modbus/ModbusDevice;I)D + public static final fun readDoubleRegister (Lspace/kscience/controls/modbus/ModbusDevice;I)D + public static final fun readHoldingRegister (Lspace/kscience/controls/modbus/ModbusDevice;I)S + public static final fun readHoldingRegisters (Lspace/kscience/controls/modbus/ModbusDevice;II)Ljava/util/List; + public static final fun readHoldingRegistersToBuffer (Lspace/kscience/controls/modbus/ModbusDevice;II)Ljava/nio/ByteBuffer; + public static final fun readHoldingRegistersToPacket (Lspace/kscience/controls/modbus/ModbusDevice;II)Lio/ktor/utils/io/core/ByteReadPacket; + public static final fun readInputDiscrete (Lspace/kscience/controls/modbus/ModbusDevice;I)Z + public static final fun readInputDiscretes (Lspace/kscience/controls/modbus/ModbusDevice;II)Lcom/ghgande/j2mod/modbus/util/BitVector; + public static final fun readInputRegister (Lspace/kscience/controls/modbus/ModbusDevice;I)S + public static final fun readInputRegisters (Lspace/kscience/controls/modbus/ModbusDevice;II)Ljava/util/List; + public static final fun readInputRegistersToBuffer (Lspace/kscience/controls/modbus/ModbusDevice;II)Ljava/nio/ByteBuffer; + public static final fun readInputRegistersToPacket (Lspace/kscience/controls/modbus/ModbusDevice;II)Lio/ktor/utils/io/core/ByteReadPacket; + public static final fun writeCoil (Lspace/kscience/controls/modbus/ModbusDevice;IZ)V + public static final fun writeCoil (Lspace/kscience/controls/modbus/ModbusDevice;Lspace/kscience/controls/modbus/ModbusRegistryKey$Coil;Z)V + public static final fun writeCoils (Lspace/kscience/controls/modbus/ModbusDevice;I[Z)V + public static final fun writeHoldingRegister (Lspace/kscience/controls/modbus/ModbusDevice;IS)I + public static final fun writeHoldingRegister (Lspace/kscience/controls/modbus/ModbusDevice;Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRegister;S)I + public static final fun writeHoldingRegisters (Lspace/kscience/controls/modbus/ModbusDevice;ILjava/nio/ByteBuffer;)I + public static final fun writeHoldingRegisters (Lspace/kscience/controls/modbus/ModbusDevice;I[S)I + public static final fun writeShortRegister (Lspace/kscience/controls/modbus/ModbusDevice;IS)V +} + +public final class space/kscience/controls/modbus/ModbusHub : java/lang/AutoCloseable, space/kscience/controls/api/DeviceHub { + public fun (Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function0;Ljava/util/Map;)V + public fun close ()V + public final fun getContext ()Lspace/kscience/dataforge/context/Context; + public fun getDevices ()Ljava/util/Map; + public final fun getMaster ()Lcom/ghgande/j2mod/modbus/facade/AbstractModbusMaster; + public final fun getMasterBuilder ()Lkotlin/jvm/functions/Function0; + public final fun getSpecs ()Ljava/util/Map; +} + +public abstract class space/kscience/controls/modbus/ModbusRegistryKey { + public abstract fun getAddress ()I + public fun getCount ()I +} + +public final class space/kscience/controls/modbus/ModbusRegistryKey$Coil : space/kscience/controls/modbus/ModbusRegistryKey { + public fun (I)V + public final fun component1 ()I + public final fun copy (I)Lspace/kscience/controls/modbus/ModbusRegistryKey$Coil; + public static synthetic fun copy$default (Lspace/kscience/controls/modbus/ModbusRegistryKey$Coil;IILjava/lang/Object;)Lspace/kscience/controls/modbus/ModbusRegistryKey$Coil; + public fun equals (Ljava/lang/Object;)Z + public fun getAddress ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class space/kscience/controls/modbus/ModbusRegistryKey$DiscreteInput : space/kscience/controls/modbus/ModbusRegistryKey { + public fun (I)V + public final fun component1 ()I + public final fun copy (I)Lspace/kscience/controls/modbus/ModbusRegistryKey$DiscreteInput; + public static synthetic fun copy$default (Lspace/kscience/controls/modbus/ModbusRegistryKey$DiscreteInput;IILjava/lang/Object;)Lspace/kscience/controls/modbus/ModbusRegistryKey$DiscreteInput; + public fun equals (Ljava/lang/Object;)Z + public fun getAddress ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class space/kscience/controls/modbus/ModbusRegistryKey$HoldingRange : space/kscience/controls/modbus/ModbusRegistryKey$HoldingRegister { + public fun (IILspace/kscience/dataforge/io/IOFormat;)V + public fun getCount ()I + public final fun getEndAddress ()I + public final fun getFormat ()Lspace/kscience/dataforge/io/IOFormat; + public fun toString ()Ljava/lang/String; +} + +public class space/kscience/controls/modbus/ModbusRegistryKey$HoldingRegister : space/kscience/controls/modbus/ModbusRegistryKey { + public fun (I)V + public fun getAddress ()I + public fun toString ()Ljava/lang/String; +} + +public final class space/kscience/controls/modbus/ModbusRegistryKey$InputRange : space/kscience/controls/modbus/ModbusRegistryKey$InputRegister { + public fun (IILspace/kscience/dataforge/io/IOFormat;)V + public fun getCount ()I + public final fun getEndAddress ()I + public final fun getFormat ()Lspace/kscience/dataforge/io/IOFormat; + public fun toString ()Ljava/lang/String; +} + +public class space/kscience/controls/modbus/ModbusRegistryKey$InputRegister : space/kscience/controls/modbus/ModbusRegistryKey { + public fun (I)V + public fun getAddress ()I + public fun toString ()Ljava/lang/String; +} + +public abstract class space/kscience/controls/modbus/ModbusRegistryMap { + public static final field Companion Lspace/kscience/controls/modbus/ModbusRegistryMap$Companion; + public fun ()V + protected final fun coil (ILjava/lang/String;)Lspace/kscience/controls/modbus/ModbusRegistryKey$Coil; + public static synthetic fun coil$default (Lspace/kscience/controls/modbus/ModbusRegistryMap;ILjava/lang/String;ILjava/lang/Object;)Lspace/kscience/controls/modbus/ModbusRegistryKey$Coil; + protected final fun discrete (ILjava/lang/String;)Lspace/kscience/controls/modbus/ModbusRegistryKey$DiscreteInput; + public static synthetic fun discrete$default (Lspace/kscience/controls/modbus/ModbusRegistryMap;ILjava/lang/String;ILjava/lang/Object;)Lspace/kscience/controls/modbus/ModbusRegistryKey$DiscreteInput; + public final fun getEntries ()Ljava/util/Map; + protected final fun input (IILspace/kscience/dataforge/io/IOFormat;Ljava/lang/String;)Lspace/kscience/controls/modbus/ModbusRegistryKey$InputRange; + protected final fun input (ILjava/lang/String;)Lspace/kscience/controls/modbus/ModbusRegistryKey$InputRegister; + public static synthetic fun input$default (Lspace/kscience/controls/modbus/ModbusRegistryMap;IILspace/kscience/dataforge/io/IOFormat;Ljava/lang/String;ILjava/lang/Object;)Lspace/kscience/controls/modbus/ModbusRegistryKey$InputRange; + public static synthetic fun input$default (Lspace/kscience/controls/modbus/ModbusRegistryMap;ILjava/lang/String;ILjava/lang/Object;)Lspace/kscience/controls/modbus/ModbusRegistryKey$InputRegister; + protected final fun register (IILspace/kscience/dataforge/io/IOFormat;Ljava/lang/String;)Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRange; + protected final fun register (ILjava/lang/String;)Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRegister; + protected final fun register (Lspace/kscience/controls/modbus/ModbusRegistryKey;Ljava/lang/String;)Lspace/kscience/controls/modbus/ModbusRegistryKey; + public static synthetic fun register$default (Lspace/kscience/controls/modbus/ModbusRegistryMap;IILspace/kscience/dataforge/io/IOFormat;Ljava/lang/String;ILjava/lang/Object;)Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRange; + public static synthetic fun register$default (Lspace/kscience/controls/modbus/ModbusRegistryMap;ILjava/lang/String;ILjava/lang/Object;)Lspace/kscience/controls/modbus/ModbusRegistryKey$HoldingRegister; +} + +public final class space/kscience/controls/modbus/ModbusRegistryMap$Companion { + public final fun print (Lspace/kscience/controls/modbus/ModbusRegistryMap;Ljava/lang/Appendable;)V + public static synthetic fun print$default (Lspace/kscience/controls/modbus/ModbusRegistryMap$Companion;Lspace/kscience/controls/modbus/ModbusRegistryMap;Ljava/lang/Appendable;ILjava/lang/Object;)V + public final fun validate (Lspace/kscience/controls/modbus/ModbusRegistryMap;)V +} + diff --git a/controls-opcua/api/controls-opcua.api b/controls-opcua/api/controls-opcua.api new file mode 100644 index 0000000..6ec2ef5 --- /dev/null +++ b/controls-opcua/api/controls-opcua.api @@ -0,0 +1,82 @@ +public final class space/kscience/controls/opcua/client/MetaBsdParser : org/eclipse/milo/opcua/binaryschema/parser/BsdParser { + public fun ()V +} + +public final class space/kscience/controls/opcua/client/MetaBsdParserKt { + public static final fun toMeta (Lorg/eclipse/milo/opcua/stack/core/types/builtin/Variant;Lorg/eclipse/milo/opcua/stack/core/serialization/SerializationContext;)Lspace/kscience/dataforge/meta/Meta; +} + +public final class space/kscience/controls/opcua/client/MiloConfiguration : space/kscience/dataforge/meta/Scheme { + public static final field Companion Lspace/kscience/controls/opcua/client/MiloConfiguration$Companion; + public fun ()V + public final fun getEndpointUrl ()Ljava/lang/String; + public final fun getSecurityPolicy ()Lorg/eclipse/milo/opcua/stack/core/security/SecurityPolicy; + public final fun getUsername ()Lspace/kscience/controls/opcua/client/MiloUsername; + public final fun setEndpointUrl (Ljava/lang/String;)V + public final fun setSecurityPolicy (Lorg/eclipse/milo/opcua/stack/core/security/SecurityPolicy;)V + public final fun setUsername (Lspace/kscience/controls/opcua/client/MiloUsername;)V +} + +public final class space/kscience/controls/opcua/client/MiloConfiguration$Companion : space/kscience/dataforge/meta/SchemeSpec { +} + +public abstract class space/kscience/controls/opcua/client/MiloIdentity : space/kscience/dataforge/meta/Scheme { +} + +public final class space/kscience/controls/opcua/client/MiloUsername : space/kscience/controls/opcua/client/MiloIdentity { + public static final field Companion Lspace/kscience/controls/opcua/client/MiloUsername$Companion; + public fun ()V + public final fun getPassword ()Ljava/lang/String; + public final fun getUsername ()Ljava/lang/String; + public final fun setPassword (Ljava/lang/String;)V + public final fun setUsername (Ljava/lang/String;)V +} + +public final class space/kscience/controls/opcua/client/MiloUsername$Companion : space/kscience/dataforge/meta/SchemeSpec { +} + +public abstract interface class space/kscience/controls/opcua/client/OpcUaDevice : space/kscience/controls/api/Device { + public abstract fun getClient ()Lorg/eclipse/milo/opcua/sdk/client/OpcUaClient; +} + +public class space/kscience/controls/opcua/client/OpcUaDeviceBySpec : space/kscience/controls/spec/DeviceBySpec, space/kscience/controls/opcua/client/OpcUaDevice { + public fun (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/controls/opcua/client/MiloConfiguration;Lspace/kscience/dataforge/context/Context;)V + public synthetic fun (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/controls/opcua/client/MiloConfiguration;Lspace/kscience/dataforge/context/Context;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun close ()V + public fun getClient ()Lorg/eclipse/milo/opcua/sdk/client/OpcUaClient; +} + +public final class space/kscience/controls/opcua/client/OpcUaDeviceKt { + public static final fun opcDouble (Lspace/kscience/controls/opcua/client/OpcUaDevice;Lorg/eclipse/milo/opcua/stack/core/types/builtin/NodeId;D)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun opcDouble$default (Lspace/kscience/controls/opcua/client/OpcUaDevice;Lorg/eclipse/milo/opcua/stack/core/types/builtin/NodeId;DILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static final fun opcInt (Lspace/kscience/controls/opcua/client/OpcUaDevice;Lorg/eclipse/milo/opcua/stack/core/types/builtin/NodeId;D)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun opcInt$default (Lspace/kscience/controls/opcua/client/OpcUaDevice;Lorg/eclipse/milo/opcua/stack/core/types/builtin/NodeId;DILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; + public static final fun opcString (Lspace/kscience/controls/opcua/client/OpcUaDevice;Lorg/eclipse/milo/opcua/stack/core/types/builtin/NodeId;D)Lkotlin/properties/ReadWriteProperty; + public static synthetic fun opcString$default (Lspace/kscience/controls/opcua/client/OpcUaDevice;Lorg/eclipse/milo/opcua/stack/core/types/builtin/NodeId;DILjava/lang/Object;)Lkotlin/properties/ReadWriteProperty; +} + +public final class space/kscience/controls/opcua/server/DeviceNameSpace : org/eclipse/milo/opcua/sdk/server/api/ManagedNamespaceWithLifecycle { + public static final field Companion Lspace/kscience/controls/opcua/server/DeviceNameSpace$Companion; + public static final field NAMESPACE_URI Ljava/lang/String; + public fun (Lorg/eclipse/milo/opcua/sdk/server/OpcUaServer;Lspace/kscience/controls/manager/DeviceManager;)V + public final fun getDeviceManager ()Lspace/kscience/controls/manager/DeviceManager; + public fun onDataItemsCreated (Ljava/util/List;)V + public fun onDataItemsDeleted (Ljava/util/List;)V + public fun onDataItemsModified (Ljava/util/List;)V + public fun onMonitoringModeChanged (Ljava/util/List;)V +} + +public final class space/kscience/controls/opcua/server/DeviceNameSpace$Companion { +} + +public final class space/kscience/controls/opcua/server/DeviceNameSpaceKt { + public static final fun get (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/api/PropertyDescriptor;)Lspace/kscience/dataforge/meta/Meta; + public static final fun read (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/api/PropertyDescriptor;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun serveDevices (Lorg/eclipse/milo/opcua/sdk/server/OpcUaServer;Lspace/kscience/controls/manager/DeviceManager;)Lspace/kscience/controls/opcua/server/DeviceNameSpace; +} + +public final class space/kscience/controls/opcua/server/ServerUtilsKt { + public static final fun OpcUaServer (Lkotlin/jvm/functions/Function1;)Lorg/eclipse/milo/opcua/sdk/server/OpcUaServer; + public static final fun endpoint (Lorg/eclipse/milo/opcua/sdk/server/api/config/OpcUaServerConfigBuilder;Lkotlin/jvm/functions/Function1;)V +} + diff --git a/controls-pi/api/controls-pi.api b/controls-pi/api/controls-pi.api new file mode 100644 index 0000000..2fdaf2d --- /dev/null +++ b/controls-pi/api/controls-pi.api @@ -0,0 +1,28 @@ +public final class space/kscience/controls/pi/PiPlugin : space/kscience/dataforge/context/AbstractPlugin { + public static final field Companion Lspace/kscience/controls/pi/PiPlugin$Companion; + public fun ()V + public final fun getPorts ()Lspace/kscience/controls/ports/Ports; + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; +} + +public final class space/kscience/controls/pi/PiPlugin$Companion : space/kscience/dataforge/context/PluginFactory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/pi/PiPlugin; + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; +} + +public final class space/kscience/controls/pi/PiSerialPort : space/kscience/controls/ports/AbstractPort { + public static final field Companion Lspace/kscience/controls/pi/PiSerialPort$Companion; + public fun (Lspace/kscience/dataforge/context/Context;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function0;)V + public synthetic fun (Lspace/kscience/dataforge/context/Context;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun close ()V + public final fun getSerialBuilder ()Lkotlin/jvm/functions/Function0; +} + +public final class space/kscience/controls/pi/PiSerialPort$Companion : space/kscience/controls/ports/PortFactory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/ports/Port; + public fun getType ()Ljava/lang/String; + public final fun open (Lspace/kscience/dataforge/context/Context;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)Lspace/kscience/controls/pi/PiSerialPort; +} + diff --git a/controls-ports-ktor/api/controls-ports-ktor.api b/controls-ports-ktor/api/controls-ports-ktor.api new file mode 100644 index 0000000..ecce329 --- /dev/null +++ b/controls-ports-ktor/api/controls-ports-ktor.api @@ -0,0 +1,47 @@ +public final class space/kscience/controls/ports/KtorPortsPlugin : space/kscience/dataforge/context/AbstractPlugin { + public static final field Companion Lspace/kscience/controls/ports/KtorPortsPlugin$Companion; + public fun ()V + public fun content (Ljava/lang/String;)Ljava/util/Map; + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; +} + +public final class space/kscience/controls/ports/KtorPortsPlugin$Companion : space/kscience/dataforge/context/PluginFactory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/ports/KtorPortsPlugin; + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; +} + +public final class space/kscience/controls/ports/KtorTcpPort : space/kscience/controls/ports/AbstractPort, java/io/Closeable { + public static final field Companion Lspace/kscience/controls/ports/KtorTcpPort$Companion; + public fun close ()V + public final fun getHost ()Ljava/lang/String; + public final fun getPort ()I + public fun toString ()Ljava/lang/String; +} + +public final class space/kscience/controls/ports/KtorTcpPort$Companion : space/kscience/controls/ports/PortFactory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/ports/Port; + public fun getType ()Ljava/lang/String; + public final fun open (Lspace/kscience/dataforge/context/Context;Ljava/lang/String;ILkotlin/coroutines/CoroutineContext;)Lspace/kscience/controls/ports/KtorTcpPort; + public static synthetic fun open$default (Lspace/kscience/controls/ports/KtorTcpPort$Companion;Lspace/kscience/dataforge/context/Context;Ljava/lang/String;ILkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lspace/kscience/controls/ports/KtorTcpPort; +} + +public final class space/kscience/controls/ports/KtorUdpPort : space/kscience/controls/ports/AbstractPort, java/io/Closeable { + public static final field Companion Lspace/kscience/controls/ports/KtorUdpPort$Companion; + public fun close ()V + public final fun getLocalHost ()Ljava/lang/String; + public final fun getLocalPort ()Ljava/lang/Integer; + public final fun getRemoteHost ()Ljava/lang/String; + public final fun getRemotePort ()I + public fun toString ()Ljava/lang/String; +} + +public final class space/kscience/controls/ports/KtorUdpPort$Companion : space/kscience/controls/ports/PortFactory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/ports/Port; + public fun getType ()Ljava/lang/String; + public final fun open (Lspace/kscience/dataforge/context/Context;Ljava/lang/String;ILjava/lang/Integer;Ljava/lang/String;Lkotlin/coroutines/CoroutineContext;)Lspace/kscience/controls/ports/KtorUdpPort; + public static synthetic fun open$default (Lspace/kscience/controls/ports/KtorUdpPort$Companion;Lspace/kscience/dataforge/context/Context;Ljava/lang/String;ILjava/lang/Integer;Ljava/lang/String;Lkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lspace/kscience/controls/ports/KtorUdpPort; +} + diff --git a/controls-serial/api/controls-serial.api b/controls-serial/api/controls-serial.api new file mode 100644 index 0000000..636b825 --- /dev/null +++ b/controls-serial/api/controls-serial.api @@ -0,0 +1,29 @@ +public final class space/kscience/controls/serial/JSerialCommPort : space/kscience/controls/ports/AbstractPort { + public static final field Companion Lspace/kscience/controls/serial/JSerialCommPort$Companion; + public fun (Lspace/kscience/dataforge/context/Context;Lcom/fazecast/jSerialComm/SerialPort;Lkotlin/coroutines/CoroutineContext;)V + public synthetic fun (Lspace/kscience/dataforge/context/Context;Lcom/fazecast/jSerialComm/SerialPort;Lkotlin/coroutines/CoroutineContext;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun close ()V + public fun toString ()Ljava/lang/String; +} + +public final class space/kscience/controls/serial/JSerialCommPort$Companion : space/kscience/controls/ports/PortFactory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/ports/Port; + public fun getType ()Ljava/lang/String; + public final fun open (Lspace/kscience/dataforge/context/Context;Ljava/lang/String;IIIILkotlin/coroutines/CoroutineContext;)Lspace/kscience/controls/serial/JSerialCommPort; + public static synthetic fun open$default (Lspace/kscience/controls/serial/JSerialCommPort$Companion;Lspace/kscience/dataforge/context/Context;Ljava/lang/String;IIIILkotlin/coroutines/CoroutineContext;ILjava/lang/Object;)Lspace/kscience/controls/serial/JSerialCommPort; +} + +public final class space/kscience/controls/serial/SerialPortPlugin : space/kscience/dataforge/context/AbstractPlugin { + public static final field Companion Lspace/kscience/controls/serial/SerialPortPlugin$Companion; + public fun ()V + public fun content (Ljava/lang/String;)Ljava/util/Map; + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; +} + +public final class space/kscience/controls/serial/SerialPortPlugin$Companion : space/kscience/dataforge/context/PluginFactory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/serial/SerialPortPlugin; + public fun getTag ()Lspace/kscience/dataforge/context/PluginTag; +} + diff --git a/controls-server/api/controls-server.api b/controls-server/api/controls-server.api new file mode 100644 index 0000000..f94aeba --- /dev/null +++ b/controls-server/api/controls-server.api @@ -0,0 +1,9 @@ +public final class space/kscience/controls/server/DeviceWebServerKt { + public static final fun deviceManagerModule (Lio/ktor/server/application/Application;Lspace/kscience/controls/manager/DeviceManager;[Lspace/kscience/magix/api/MagixFlowPlugin;Ljava/util/Collection;Ljava/lang/String;I)V + public static synthetic fun deviceManagerModule$default (Lio/ktor/server/application/Application;Lspace/kscience/controls/manager/DeviceManager;[Lspace/kscience/magix/api/MagixFlowPlugin;Ljava/util/Collection;Ljava/lang/String;IILjava/lang/Object;)V + public static final fun getWEB_SERVER_TARGET ()Lspace/kscience/dataforge/names/Name; + public static final fun startDeviceServer (Lkotlinx/coroutines/CoroutineScope;Lspace/kscience/controls/manager/DeviceManager;ILjava/lang/String;)Lio/ktor/server/engine/ApplicationEngine; + public static synthetic fun startDeviceServer$default (Lkotlinx/coroutines/CoroutineScope;Lspace/kscience/controls/manager/DeviceManager;ILjava/lang/String;ILjava/lang/Object;)Lio/ktor/server/engine/ApplicationEngine; + public static final fun whenStarted (Lio/ktor/server/engine/ApplicationEngine;Lkotlin/jvm/functions/Function1;)V +} + diff --git a/demo/all-things/api/all-things.api b/demo/all-things/api/all-things.api new file mode 100644 index 0000000..4d283e5 --- /dev/null +++ b/demo/all-things/api/all-things.api @@ -0,0 +1,77 @@ +public final class space/kscience/controls/demo/DemoController : tornadofx/Controller, space/kscience/dataforge/context/ContextAware { + public fun ()V + public fun getContext ()Lspace/kscience/dataforge/context/Context; + public final fun getDevice ()Lspace/kscience/controls/demo/DemoDevice; + public final fun getMagixServer ()Lio/ktor/server/engine/ApplicationEngine; + public final fun getOpcUaServer ()Lorg/eclipse/milo/opcua/sdk/server/OpcUaServer; + public final fun getVisualizer ()Lio/ktor/server/engine/ApplicationEngine; + public final fun init ()V + public final fun setDevice (Lspace/kscience/controls/demo/DemoDevice;)V + public final fun setMagixServer (Lio/ktor/server/engine/ApplicationEngine;)V + public final fun setOpcUaServer (Lorg/eclipse/milo/opcua/sdk/server/OpcUaServer;)V + public final fun setVisualizer (Lio/ktor/server/engine/ApplicationEngine;)V + public final fun shutdown ()V +} + +public final class space/kscience/controls/demo/DemoControllerApp : tornadofx/App { + public fun ()V + public fun start (Ljavafx/stage/Stage;)V + public fun stop ()V +} + +public final class space/kscience/controls/demo/DemoControllerView : tornadofx/View { + public fun ()V + public fun getRoot ()Ljavafx/scene/Parent; +} + +public final class space/kscience/controls/demo/DemoControllerViewKt { + public static final fun main ()V + public static synthetic fun main ([Ljava/lang/String;)V +} + +public final class space/kscience/controls/demo/DemoDevice : space/kscience/controls/spec/DeviceBySpec, space/kscience/controls/demo/IDemoDevice { + public static final field Companion Lspace/kscience/controls/demo/DemoDevice$Companion; + public fun (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)V + public fun cosValue ()D + public fun getCosScaleState ()D + public fun getSinScaleState ()D + public fun getTimeScaleState ()D + public fun setCosScaleState (D)V + public fun setSinScaleState (D)V + public fun setTimeScaleState (D)V + public fun sinValue ()D +} + +public final class space/kscience/controls/demo/DemoDevice$Companion : space/kscience/controls/spec/DeviceSpec, space/kscience/dataforge/context/Factory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/demo/DemoDevice; + public final fun getCoordinates ()Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun getCos ()Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun getCosScale ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; + public final fun getResetScale ()Lspace/kscience/controls/spec/DeviceActionSpec; + public final fun getSin ()Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun getSinScale ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; + public final fun getTimeScale ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; + public synthetic fun onOpen (Lspace/kscience/controls/api/Device;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun onOpen (Lspace/kscience/controls/demo/IDemoDevice;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/demo/DemoDeviceServerKt { + public static final fun startDemoDeviceServer (Lkotlinx/coroutines/CoroutineScope;Lspace/kscience/magix/api/MagixEndpoint;)Lio/ktor/server/engine/ApplicationEngine; + public static final fun updateFrom (Lspace/kscience/plotly/models/Trace;Ljava/lang/String;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun updateXYFrom (Lspace/kscience/plotly/models/Trace;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun windowed (Lkotlinx/coroutines/flow/Flow;I)Lkotlinx/coroutines/flow/Flow; +} + +public abstract interface class space/kscience/controls/demo/IDemoDevice : space/kscience/controls/api/Device { + public abstract fun cosValue ()D + public abstract fun getCosScaleState ()D + public abstract fun getSinScaleState ()D + public abstract fun getTimeScaleState ()D + public abstract fun setCosScaleState (D)V + public abstract fun setSinScaleState (D)V + public abstract fun setTimeScaleState (D)V + public abstract fun sinValue ()D + public fun time ()Ljava/time/Instant; +} + diff --git a/demo/car/api/car.api b/demo/car/api/car.api new file mode 100644 index 0000000..5b1940f --- /dev/null +++ b/demo/car/api/car.api @@ -0,0 +1,111 @@ +public abstract interface class space/kscience/controls/demo/car/IVirtualCar : space/kscience/controls/api/Device { + public static final field Companion Lspace/kscience/controls/demo/car/IVirtualCar$Companion; + public abstract fun getAccelerationState ()Lspace/kscience/controls/demo/car/Vector2D; + public abstract fun getLocationState ()Lspace/kscience/controls/demo/car/Vector2D; + public abstract fun getSpeedState ()Lspace/kscience/controls/demo/car/Vector2D; + public abstract fun setAccelerationState (Lspace/kscience/controls/demo/car/Vector2D;)V + public abstract fun setLocationState (Lspace/kscience/controls/demo/car/Vector2D;)V + public abstract fun setSpeedState (Lspace/kscience/controls/demo/car/Vector2D;)V +} + +public final class space/kscience/controls/demo/car/IVirtualCar$Companion : space/kscience/controls/spec/DeviceSpec { + public final fun getAcceleration ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; + public final fun getLocation ()Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun getSpeed ()Lspace/kscience/controls/spec/DevicePropertySpec; +} + +public final class space/kscience/controls/demo/car/MagixVirtualCar : space/kscience/controls/demo/car/VirtualCar { + public static final field Companion Lspace/kscience/controls/demo/car/MagixVirtualCar$Companion; + public fun (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)V + public fun open (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/demo/car/MagixVirtualCar$Companion : space/kscience/dataforge/context/Factory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/demo/car/MagixVirtualCar; +} + +public final class space/kscience/controls/demo/car/Vector2D : space/kscience/dataforge/meta/MetaRepr { + public static final field CoordinatesMetaConverter Lspace/kscience/controls/demo/car/Vector2D$CoordinatesMetaConverter; + public fun ()V + public fun (DD)V + public synthetic fun (DDILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()D + public final fun component2 ()D + public final fun copy (DD)Lspace/kscience/controls/demo/car/Vector2D; + public static synthetic fun copy$default (Lspace/kscience/controls/demo/car/Vector2D;DDILjava/lang/Object;)Lspace/kscience/controls/demo/car/Vector2D; + public final fun div (D)Lspace/kscience/controls/demo/car/Vector2D; + public fun equals (Ljava/lang/Object;)Z + public final fun getX ()D + public final fun getY ()D + public fun hashCode ()I + public final fun setX (D)V + public final fun setY (D)V + public fun toMeta ()Lspace/kscience/dataforge/meta/Meta; + public fun toString ()Ljava/lang/String; +} + +public final class space/kscience/controls/demo/car/Vector2D$CoordinatesMetaConverter : space/kscience/dataforge/meta/transformations/MetaConverter { + public synthetic fun metaToObject (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun metaToObject (Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/demo/car/Vector2D; + public synthetic fun objectToMeta (Ljava/lang/Object;)Lspace/kscience/dataforge/meta/Meta; + public fun objectToMeta (Lspace/kscience/controls/demo/car/Vector2D;)Lspace/kscience/dataforge/meta/Meta; +} + +public class space/kscience/controls/demo/car/VirtualCar : space/kscience/controls/spec/DeviceBySpec, space/kscience/controls/demo/car/IVirtualCar { + public static final field Companion Lspace/kscience/controls/demo/car/VirtualCar$Companion; + public fun (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)V + public final fun applyForce-HG0u8IE (Lspace/kscience/controls/demo/car/Vector2D;J)V + public fun getAccelerationState ()Lspace/kscience/controls/demo/car/Vector2D; + public fun getLocationState ()Lspace/kscience/controls/demo/car/Vector2D; + public fun getSpeedState ()Lspace/kscience/controls/demo/car/Vector2D; + public fun open (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun setAccelerationState (Lspace/kscience/controls/demo/car/Vector2D;)V + public fun setLocationState (Lspace/kscience/controls/demo/car/Vector2D;)V + public fun setSpeedState (Lspace/kscience/controls/demo/car/Vector2D;)V +} + +public final class space/kscience/controls/demo/car/VirtualCar$Companion : space/kscience/dataforge/context/Factory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/demo/car/VirtualCar; +} + +public final class space/kscience/controls/demo/car/VirtualCarController : tornadofx/Controller, space/kscience/dataforge/context/ContextAware { + public static final field Companion Lspace/kscience/controls/demo/car/VirtualCarController$Companion; + public fun ()V + public fun getContext ()Lspace/kscience/dataforge/context/Context; + public final fun getMagixServer ()Lio/ktor/server/engine/ApplicationEngine; + public final fun getMagixVirtualCar ()Lspace/kscience/controls/demo/car/MagixVirtualCar; + public final fun getStorageEndpoint ()Lspace/kscience/magix/api/MagixEndpoint; + public final fun getVirtualCar ()Lspace/kscience/controls/demo/car/VirtualCar; + public final fun getXodusStorageJob ()Lkotlinx/coroutines/Job; + public final fun init ()V + public final fun setMagixServer (Lio/ktor/server/engine/ApplicationEngine;)V + public final fun setMagixVirtualCar (Lspace/kscience/controls/demo/car/MagixVirtualCar;)V + public final fun setStorageEndpoint (Lspace/kscience/magix/api/MagixEndpoint;)V + public final fun setVirtualCar (Lspace/kscience/controls/demo/car/VirtualCar;)V + public final fun setXodusStorageJob (Lkotlinx/coroutines/Job;)V + public final fun shutdown ()V +} + +public final class space/kscience/controls/demo/car/VirtualCarController$Companion { + public final fun getDeviceEntityStorePath ()Ljava/nio/file/Path; + public final fun getMagixEntityStorePath ()Ljava/nio/file/Path; +} + +public final class space/kscience/controls/demo/car/VirtualCarControllerApp : tornadofx/App { + public fun ()V + public fun start (Ljavafx/stage/Stage;)V + public fun stop ()V +} + +public final class space/kscience/controls/demo/car/VirtualCarControllerKt { + public static final fun main ()V + public static synthetic fun main ([Ljava/lang/String;)V +} + +public final class space/kscience/controls/demo/car/VirtualCarControllerView : tornadofx/View { + public fun ()V + public fun getRoot ()Ljavafx/scene/Parent; +} + diff --git a/demo/echo/api/echo.api b/demo/echo/api/echo.api new file mode 100644 index 0000000..bc0fe05 --- /dev/null +++ b/demo/echo/api/echo.api @@ -0,0 +1,5 @@ +public final class space/kscience/controls/demo/echo/MainKt { + public static final fun main (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun main ([Ljava/lang/String;)V +} + diff --git a/demo/magix-demo/api/magix-demo.api b/demo/magix-demo/api/magix-demo.api new file mode 100644 index 0000000..7a1c19e --- /dev/null +++ b/demo/magix-demo/api/magix-demo.api @@ -0,0 +1,7 @@ +public final class ZmqKt { + public static final fun main (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun main ([Ljava/lang/String;)V + public static final fun sendJson (Lspace/kscience/magix/api/MagixEndpoint;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sendJson$default (Lspace/kscience/magix/api/MagixEndpoint;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; +} + diff --git a/demo/many-devices/api/many-devices.api b/demo/many-devices/api/many-devices.api new file mode 100644 index 0000000..1b398a2 --- /dev/null +++ b/demo/many-devices/api/many-devices.api @@ -0,0 +1,18 @@ +public final class space/kscience/controls/demo/MassDevice : space/kscience/controls/spec/DeviceBySpec { + public static final field Companion Lspace/kscience/controls/demo/MassDevice$Companion; + public fun (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)V +} + +public final class space/kscience/controls/demo/MassDevice$Companion : space/kscience/controls/spec/DeviceSpec, space/kscience/dataforge/context/Factory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lspace/kscience/controls/demo/MassDevice; + public final fun getValue ()Lspace/kscience/controls/spec/DevicePropertySpec; + public synthetic fun onOpen (Lspace/kscience/controls/api/Device;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun onOpen (Lspace/kscience/controls/demo/MassDevice;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/controls/demo/MassDeviceKt { + public static final fun main (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun main ([Ljava/lang/String;)V +} + diff --git a/demo/mks-pdr900/api/mks-pdr900.api b/demo/mks-pdr900/api/mks-pdr900.api new file mode 100644 index 0000000..a9c9ecb --- /dev/null +++ b/demo/mks-pdr900/api/mks-pdr900.api @@ -0,0 +1,28 @@ +public final class center/sciprog/devices/mks/MksPdr900Device : space/kscience/controls/spec/DeviceBySpec { + public static final field Companion Lcenter/sciprog/devices/mks/MksPdr900Device$Companion; + public static final field DEFAULT_CHANNEL I + public fun (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)V + public final fun readChannelData (ILkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun readPowerOn (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun writePowerOn (ZLkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class center/sciprog/devices/mks/MksPdr900Device$Companion : space/kscience/controls/spec/DeviceSpec, space/kscience/dataforge/context/Factory { + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lcenter/sciprog/devices/mks/MksPdr900Device; + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public final fun getChannel ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; + public final fun getError ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; + public final fun getPowerOn ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; + public final fun getValue ()Lspace/kscience/controls/spec/DevicePropertySpec; + public fun onClose (Lcenter/sciprog/devices/mks/MksPdr900Device;)V + public synthetic fun onClose (Lspace/kscience/controls/api/Device;)V +} + +public final class center/sciprog/devices/mks/NullableStringMetaConverter : space/kscience/dataforge/meta/transformations/MetaConverter { + public static final field INSTANCE Lcenter/sciprog/devices/mks/NullableStringMetaConverter; + public synthetic fun metaToObject (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun metaToObject (Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/String; + public synthetic fun objectToMeta (Ljava/lang/Object;)Lspace/kscience/dataforge/meta/Meta; + public fun objectToMeta (Ljava/lang/String;)Lspace/kscience/dataforge/meta/Meta; +} + diff --git a/demo/motors/api/motors.api b/demo/motors/api/motors.api new file mode 100644 index 0000000..506d7ee --- /dev/null +++ b/demo/motors/api/motors.api @@ -0,0 +1,115 @@ +public final class ru/mipt/npm/devices/pimotionmaster/FxDevicePropertiesKt { + public static final fun fxProperty (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/spec/DevicePropertySpec;)Ljavafx/beans/property/ReadOnlyProperty; + public static final fun fxProperty (Lspace/kscience/controls/api/Device;Lspace/kscience/controls/spec/WritableDevicePropertySpec;)Ljavafx/beans/property/Property; +} + +public final class ru/mipt/npm/devices/pimotionmaster/PiDebugServerKt { + public static final fun getExceptionHandler ()Lkotlinx/coroutines/CoroutineExceptionHandler; + public static final fun launchPiDebugServer (Lspace/kscience/dataforge/context/Context;ILjava/util/List;)Lkotlinx/coroutines/Job; + public static final fun main ()V + public static synthetic fun main ([Ljava/lang/String;)V +} + +public final class ru/mipt/npm/devices/pimotionmaster/PiMotionMasterApp : tornadofx/App { + public fun ()V +} + +public final class ru/mipt/npm/devices/pimotionmaster/PiMotionMasterAppKt { + public static final fun axisPane (Ljavafx/scene/Parent;Ljava/util/Map;Lkotlinx/coroutines/CoroutineScope;)V + public static final fun main ()V + public static synthetic fun main ([Ljava/lang/String;)V + public static final fun piMotionMasterAxis (Ljavafx/scene/layout/VBox;Ljava/lang/String;Lru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice$Axis;Lkotlinx/coroutines/CoroutineScope;)Ljavafx/scene/layout/HBox; +} + +public final class ru/mipt/npm/devices/pimotionmaster/PiMotionMasterController : tornadofx/Controller { + public fun ()V + public final fun getContext ()Lspace/kscience/dataforge/context/Context; + public final fun getDeviceManager ()Lspace/kscience/controls/manager/DeviceManager; + public final fun getMotionMaster ()Lru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice; +} + +public final class ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice : space/kscience/controls/spec/DeviceBySpec, space/kscience/controls/api/DeviceHub { + public static final field Companion Lru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice$Companion; + public fun (Lspace/kscience/dataforge/context/Context;Lspace/kscience/controls/ports/PortFactory;)V + public synthetic fun (Lspace/kscience/dataforge/context/Context;Lspace/kscience/controls/ports/PortFactory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun connect (Ljava/lang/String;I)V + public final fun disconnect ()V + public final fun getAxes ()Ljava/util/Map; + public fun getDevices ()Ljava/util/Map; + public final fun getErrorCode (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun getTimeoutValue-UwyO8pc ()J + public final fun setTimeoutValue-LRDsOJo (J)V +} + +public final class ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice$Axis : space/kscience/controls/spec/DeviceBySpec { + public static final field Companion Lru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice$Axis$Companion; + public fun (Lru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice;Ljava/lang/String;)V + public final fun getAxisId ()Ljava/lang/String; + public final fun getMm ()Lru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice; + public final fun move (DLkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice$Axis$Companion : space/kscience/controls/spec/DeviceSpec { + public final fun getClosedLoop ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; + public final fun getEnabled ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; + public final fun getHalt ()Lspace/kscience/controls/spec/DeviceActionSpec; + public final fun getMaxPosition ()Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun getMinPosition ()Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun getMove ()Lspace/kscience/controls/spec/DeviceActionSpec; + public final fun getMoveToReference ()Lspace/kscience/controls/spec/DeviceActionSpec; + public final fun getOnTarget ()Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun getOpenLoopTarget ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; + public final fun getPosition ()Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun getReference ()Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun getTargetPosition ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; + public final fun getVelocity ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; +} + +public final class ru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice$Companion : space/kscience/controls/spec/DeviceSpec, space/kscience/dataforge/context/Factory { + public synthetic fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Ljava/lang/Object; + public fun build (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)Lru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice; + public final fun getConnect ()Lspace/kscience/controls/spec/DeviceActionSpec; + public final fun getConnected ()Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun getDisconnect ()Lspace/kscience/controls/spec/DeviceActionSpec; + public final fun getFirmwareVersion ()Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun getIdentity ()Lspace/kscience/controls/spec/DevicePropertySpec; + public final fun getInitialize ()Lspace/kscience/controls/spec/DeviceActionSpec; + public final fun getStop ()Lspace/kscience/controls/spec/DeviceActionSpec; + public final fun getTimeout ()Lspace/kscience/controls/spec/WritableDevicePropertySpec; +} + +public final class ru/mipt/npm/devices/pimotionmaster/PiMotionMasterView : tornadofx/View { + public fun ()V + public final fun getDevice ()Lru/mipt/npm/devices/pimotionmaster/PiMotionMasterDevice; + public fun getRoot ()Ljavafx/scene/Parent; +} + +public final class ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice : ru/mipt/npm/devices/pimotionmaster/VirtualDevice, space/kscience/dataforge/context/ContextAware { + public static final field Companion Lru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice$Companion; + public fun (Lspace/kscience/dataforge/context/Context;Ljava/util/List;Lkotlinx/coroutines/CoroutineScope;)V + public synthetic fun (Lspace/kscience/dataforge/context/Context;Ljava/util/List;Lkotlinx/coroutines/CoroutineScope;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getContext ()Lspace/kscience/dataforge/context/Context; +} + +public final class ru/mipt/npm/devices/pimotionmaster/PiMotionMasterVirtualDevice$Companion { +} + +public abstract class ru/mipt/npm/devices/pimotionmaster/VirtualDevice : space/kscience/controls/api/Socket { + public fun (Lkotlinx/coroutines/CoroutineScope;)V + public fun close ()V + protected abstract fun evaluateRequest ([BLkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun getScope ()Lkotlinx/coroutines/CoroutineScope; + public fun isOpen ()Z + public fun receiving ()Lkotlinx/coroutines/flow/Flow; + protected final fun respond ([BLkotlin/coroutines/Continuation;)Ljava/lang/Object; + protected final fun respondInFuture-VtjQ1oo (JLkotlin/jvm/functions/Function1;)Lkotlinx/coroutines/Job; + public synthetic fun send (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun send ([BLkotlin/coroutines/Continuation;)Ljava/lang/Object; + protected fun transformRequests (Lkotlinx/coroutines/flow/Flow;)Lkotlinx/coroutines/flow/Flow; +} + +public final class ru/mipt/npm/devices/pimotionmaster/VirtualPort : space/kscience/controls/ports/AbstractPort { + public fun (Lru/mipt/npm/devices/pimotionmaster/VirtualDevice;Lspace/kscience/dataforge/context/Context;)V + public fun close ()V +} + diff --git a/magix/magix-api/api/magix-api.api b/magix/magix-api/api/magix-api.api new file mode 100644 index 0000000..dabc4cc --- /dev/null +++ b/magix/magix-api/api/magix-api.api @@ -0,0 +1,271 @@ +public abstract interface class space/kscience/magix/api/MagixEndpoint { + public static final field Companion Lspace/kscience/magix/api/MagixEndpoint$Companion; + public static final field DEFAULT_MAGIX_HTTP_PORT I + public static final field DEFAULT_MAGIX_RAW_PORT I + public static final field DEFAULT_MAGIX_ZMQ_PUB_PORT I + public static final field DEFAULT_MAGIX_ZMQ_PULL_PORT I + public abstract fun broadcast (Lspace/kscience/magix/api/MagixMessage;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun close ()V + public abstract fun subscribe (Lspace/kscience/magix/api/MagixMessageFilter;)Lkotlinx/coroutines/flow/Flow; + public static synthetic fun subscribe$default (Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/api/MagixMessageFilter;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow; +} + +public final class space/kscience/magix/api/MagixEndpoint$Companion { + public static final field DEFAULT_MAGIX_HTTP_PORT I + public static final field DEFAULT_MAGIX_RAW_PORT I + public static final field DEFAULT_MAGIX_ZMQ_PUB_PORT I + public static final field DEFAULT_MAGIX_ZMQ_PULL_PORT I + public final fun getMagixJson ()Lkotlinx/serialization/json/Json; +} + +public final class space/kscience/magix/api/MagixEndpointKt { + public static final fun send (Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/api/MagixMessage;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public abstract interface class space/kscience/magix/api/MagixFlowPlugin { + public abstract fun start (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job; + public fun start (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/MutableSharedFlow;)Lkotlinx/coroutines/Job; +} + +public final class space/kscience/magix/api/MagixFormat { + public fun (Lkotlinx/serialization/KSerializer;Ljava/util/Set;)V + public final fun component1 ()Lkotlinx/serialization/KSerializer; + public final fun component2 ()Ljava/util/Set; + public final fun copy (Lkotlinx/serialization/KSerializer;Ljava/util/Set;)Lspace/kscience/magix/api/MagixFormat; + public static synthetic fun copy$default (Lspace/kscience/magix/api/MagixFormat;Lkotlinx/serialization/KSerializer;Ljava/util/Set;ILjava/lang/Object;)Lspace/kscience/magix/api/MagixFormat; + public fun equals (Ljava/lang/Object;)Z + public final fun getDefaultFormat ()Ljava/lang/String; + public final fun getFormats ()Ljava/util/Set; + public final fun getSerializer ()Lkotlinx/serialization/KSerializer; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class space/kscience/magix/api/MagixFormatKt { + public static final fun send (Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/api/MagixFormat;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun send$default (Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/api/MagixFormat;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun subscribe (Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/api/MagixFormat;Ljava/util/Collection;Ljava/util/Collection;)Lkotlinx/coroutines/flow/Flow; + public static synthetic fun subscribe$default (Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/api/MagixFormat;Ljava/util/Collection;Ljava/util/Collection;ILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow; +} + +public final class space/kscience/magix/api/MagixMessage { + public static final field Companion Lspace/kscience/magix/api/MagixMessage$Companion; + public synthetic fun (ILjava/lang/String;Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)V + public synthetic fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lkotlinx/serialization/json/JsonElement; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Lkotlinx/serialization/json/JsonElement; + public final fun copy (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)Lspace/kscience/magix/api/MagixMessage; + public static synthetic fun copy$default (Lspace/kscience/magix/api/MagixMessage;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;ILjava/lang/Object;)Lspace/kscience/magix/api/MagixMessage; + public fun equals (Ljava/lang/Object;)Z + public final fun getFormat ()Ljava/lang/String; + public final fun getId ()Ljava/lang/String; + public final fun getParentId ()Ljava/lang/String; + public final fun getPayload ()Lkotlinx/serialization/json/JsonElement; + public final fun getSourceEndpoint ()Ljava/lang/String; + public final fun getTargetEndpoint ()Ljava/lang/String; + public final fun getUser ()Lkotlinx/serialization/json/JsonElement; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/magix/api/MagixMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/magix/api/MagixMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/magix/api/MagixMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/magix/api/MagixMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/magix/api/MagixMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/api/MagixMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/api/MagixMessageFilter { + public static final field Companion Lspace/kscience/magix/api/MagixMessageFilter$Companion; + public fun ()V + public synthetic fun (ILjava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;)V + public synthetic fun (Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun accepts (Lspace/kscience/magix/api/MagixMessage;)Z + public final fun component1 ()Ljava/util/Collection; + public final fun component2 ()Ljava/util/Collection; + public final fun component3 ()Ljava/util/Collection; + public final fun copy (Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;)Lspace/kscience/magix/api/MagixMessageFilter; + public static synthetic fun copy$default (Lspace/kscience/magix/api/MagixMessageFilter;Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;ILjava/lang/Object;)Lspace/kscience/magix/api/MagixMessageFilter; + public fun equals (Ljava/lang/Object;)Z + public final fun getFormat ()Ljava/util/Collection; + public final fun getSource ()Ljava/util/Collection; + public final fun getTarget ()Ljava/util/Collection; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/magix/api/MagixMessageFilter;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/magix/api/MagixMessageFilter$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/magix/api/MagixMessageFilter$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/magix/api/MagixMessageFilter; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/magix/api/MagixMessageFilter;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/api/MagixMessageFilter$Companion { + public final fun getALL ()Lspace/kscience/magix/api/MagixMessageFilter; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/api/MagixMessageFilterKt { + public static final fun filter (Lkotlinx/coroutines/flow/Flow;Lspace/kscience/magix/api/MagixMessageFilter;)Lkotlinx/coroutines/flow/Flow; +} + +public final class space/kscience/magix/api/MagixMessageKt { + public static final fun getUserName (Lspace/kscience/magix/api/MagixMessage;)Ljava/lang/String; +} + +public final class space/kscience/magix/services/ConvertersKt { + public static final fun launchMagixConverter (Lkotlinx/coroutines/CoroutineScope;Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/api/MagixMessageFilter;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job; + public static synthetic fun launchMagixConverter$default (Lkotlinx/coroutines/CoroutineScope;Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/api/MagixMessageFilter;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)Lkotlinx/coroutines/Job; +} + +public final class space/kscience/magix/services/MagixPortalKt { + public static final fun launchMagixPortal (Lkotlinx/coroutines/CoroutineScope;Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/api/MagixMessageFilter;Lspace/kscience/magix/api/MagixMessageFilter;)Lkotlinx/coroutines/Job; + public static synthetic fun launchMagixPortal$default (Lkotlinx/coroutines/CoroutineScope;Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/api/MagixMessageFilter;Lspace/kscience/magix/api/MagixMessageFilter;ILjava/lang/Object;)Lkotlinx/coroutines/Job; +} + +public abstract interface class space/kscience/magix/services/MagixRegistry { + public abstract fun get (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class space/kscience/magix/services/MagixRegistryErrorMessage : space/kscience/magix/services/MagixRegistryMessage { + public static final field Companion Lspace/kscience/magix/services/MagixRegistryErrorMessage$Companion; + public synthetic fun (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getErrorMessage ()Ljava/lang/String; + public final fun getErrorType ()Ljava/lang/String; + public fun getPropertyName ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/magix/services/MagixRegistryErrorMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/magix/services/MagixRegistryErrorMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/magix/services/MagixRegistryErrorMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/magix/services/MagixRegistryErrorMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/magix/services/MagixRegistryErrorMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/services/MagixRegistryErrorMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/services/MagixRegistryKt { + public static final fun getProperty (Lspace/kscience/magix/api/MagixEndpoint;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun getProperty$default (Lspace/kscience/magix/api/MagixEndpoint;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun launchMagixRegistry (Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/services/MagixRegistry;Ljava/util/Collection;Ljava/util/Collection;)Lkotlinx/coroutines/Job; + public static synthetic fun launchMagixRegistry$default (Lkotlinx/coroutines/CoroutineScope;Ljava/lang/String;Lspace/kscience/magix/api/MagixEndpoint;Lspace/kscience/magix/services/MagixRegistry;Ljava/util/Collection;Ljava/util/Collection;ILjava/lang/Object;)Lkotlinx/coroutines/Job; +} + +public abstract class space/kscience/magix/services/MagixRegistryMessage { + public static final field Companion Lspace/kscience/magix/services/MagixRegistryMessage$Companion; + public synthetic fun (ILkotlinx/serialization/internal/SerializationConstructorMarker;)V + public abstract fun getPropertyName ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/magix/services/MagixRegistryMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/magix/services/MagixRegistryMessage$Companion { + public final fun getFormat ()Lspace/kscience/magix/api/MagixFormat; + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/services/MagixRegistryModifyMessage : space/kscience/magix/services/MagixRegistryMessage { + public static final field Companion Lspace/kscience/magix/services/MagixRegistryModifyMessage$Companion; + public synthetic fun (ILjava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)V + public fun getPropertyName ()Ljava/lang/String; + public final fun getValue ()Lkotlinx/serialization/json/JsonElement; + public static final synthetic fun write$Self (Lspace/kscience/magix/services/MagixRegistryModifyMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/magix/services/MagixRegistryModifyMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/magix/services/MagixRegistryModifyMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/magix/services/MagixRegistryModifyMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/magix/services/MagixRegistryModifyMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/services/MagixRegistryModifyMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/services/MagixRegistryRequestMessage : space/kscience/magix/services/MagixRegistryMessage { + public static final field Companion Lspace/kscience/magix/services/MagixRegistryRequestMessage$Companion; + public synthetic fun (ILjava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;)V + public fun getPropertyName ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/magix/services/MagixRegistryRequestMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/magix/services/MagixRegistryRequestMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/magix/services/MagixRegistryRequestMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/magix/services/MagixRegistryRequestMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/magix/services/MagixRegistryRequestMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/services/MagixRegistryRequestMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/services/MagixRegistryValueMessage : space/kscience/magix/services/MagixRegistryMessage { + public static final field Companion Lspace/kscience/magix/services/MagixRegistryValueMessage$Companion; + public synthetic fun (ILjava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)V + public fun getPropertyName ()Ljava/lang/String; + public final fun getValue ()Lkotlinx/serialization/json/JsonElement; + public static final synthetic fun write$Self (Lspace/kscience/magix/services/MagixRegistryValueMessage;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/magix/services/MagixRegistryValueMessage$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/magix/services/MagixRegistryValueMessage$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/magix/services/MagixRegistryValueMessage; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/magix/services/MagixRegistryValueMessage;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/services/MagixRegistryValueMessage$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public abstract interface class space/kscience/magix/services/MutableMagixRegistry { + public abstract fun set (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonElement;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + diff --git a/magix/magix-java-endpoint/api/magix-java-client.api b/magix/magix-java-endpoint/api/magix-java-client.api new file mode 100644 index 0000000..5df03d9 --- /dev/null +++ b/magix/magix-java-endpoint/api/magix-java-client.api @@ -0,0 +1,7 @@ +public abstract interface class space/kscience/magix/client/MagixClient { + public abstract fun broadcast (Lspace/kscience/magix/api/MagixMessage;)V + public static fun rSocketTcp (Ljava/lang/String;I)Lspace/kscience/magix/client/MagixClient; + public static fun rSocketWs (Ljava/lang/String;ILjava/lang/String;)Lspace/kscience/magix/client/MagixClient; + public abstract fun subscribe ()Ljava/util/concurrent/Flow$Publisher; +} + diff --git a/magix/magix-rsocket/api/magix-rsocket.api b/magix/magix-rsocket/api/magix-rsocket.api new file mode 100644 index 0000000..59dacca --- /dev/null +++ b/magix/magix-rsocket/api/magix-rsocket.api @@ -0,0 +1,37 @@ +public final class space/kscience/magix/rsocket/RSocketMagixEndpoint : java/io/Closeable, space/kscience/magix/api/MagixEndpoint { + public static final field Companion Lspace/kscience/magix/rsocket/RSocketMagixEndpoint$Companion; + public fun (Lio/rsocket/kotlin/RSocket;)V + public fun broadcast (Lspace/kscience/magix/api/MagixMessage;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun close ()V + public fun subscribe (Lspace/kscience/magix/api/MagixMessageFilter;)Lkotlinx/coroutines/flow/Flow; +} + +public final class space/kscience/magix/rsocket/RSocketMagixEndpoint$Companion { +} + +public final class space/kscience/magix/rsocket/RSocketMagixEndpointKt { + public static final fun rSocketWithWebSockets (Lspace/kscience/magix/api/MagixEndpoint$Companion;Ljava/lang/String;ILjava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun rSocketWithWebSockets$default (Lspace/kscience/magix/api/MagixEndpoint$Companion;Ljava/lang/String;ILjava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; +} + +public final class space/kscience/magix/rsocket/RSocketStreamMagixEndpoint : java/io/Closeable, space/kscience/magix/api/MagixEndpoint { + public fun (Lio/rsocket/kotlin/RSocket;Lspace/kscience/magix/api/MagixMessageFilter;)V + public synthetic fun (Lio/rsocket/kotlin/RSocket;Lspace/kscience/magix/api/MagixMessageFilter;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun broadcast (Lspace/kscience/magix/api/MagixMessage;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun close ()V + public final fun getStreamFilter ()Lspace/kscience/magix/api/MagixMessageFilter; + public fun subscribe (Lspace/kscience/magix/api/MagixMessageFilter;)Lkotlinx/coroutines/flow/Flow; +} + +public final class space/kscience/magix/rsocket/RSocketStreamMagixEndpointKt { + public static final fun rSocketStreamWithWebSockets (Lspace/kscience/magix/api/MagixEndpoint$Companion;Ljava/lang/String;ILjava/lang/String;Lspace/kscience/magix/api/MagixMessageFilter;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun rSocketStreamWithWebSockets$default (Lspace/kscience/magix/api/MagixEndpoint$Companion;Ljava/lang/String;ILjava/lang/String;Lspace/kscience/magix/api/MagixMessageFilter;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; +} + +public final class space/kscience/magix/rsocket/WithTcpKt { + public static final fun rSocketStreamWithTcp (Lspace/kscience/magix/api/MagixEndpoint$Companion;Ljava/lang/String;ILspace/kscience/magix/api/MagixMessageFilter;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun rSocketStreamWithTcp$default (Lspace/kscience/magix/api/MagixEndpoint$Companion;Ljava/lang/String;ILspace/kscience/magix/api/MagixMessageFilter;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun rSocketWithTcp (Lspace/kscience/magix/api/MagixEndpoint$Companion;Ljava/lang/String;ILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun rSocketWithTcp$default (Lspace/kscience/magix/api/MagixEndpoint$Companion;Ljava/lang/String;ILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; +} + diff --git a/magix/magix-server/api/magix-server.api b/magix/magix-server/api/magix-server.api new file mode 100644 index 0000000..6ed9184 --- /dev/null +++ b/magix/magix-server/api/magix-server.api @@ -0,0 +1,45 @@ +public final class space/kscience/magix/server/MagixModuleKt { + public static final fun magixModule (Lio/ktor/server/application/Application;Ljava/lang/String;I)V + public static final fun magixModule (Lio/ktor/server/application/Application;Lkotlinx/coroutines/flow/MutableSharedFlow;Ljava/lang/String;)V + public static synthetic fun magixModule$default (Lio/ktor/server/application/Application;Ljava/lang/String;IILjava/lang/Object;)V + public static synthetic fun magixModule$default (Lio/ktor/server/application/Application;Lkotlinx/coroutines/flow/MutableSharedFlow;Ljava/lang/String;ILjava/lang/Object;)V +} + +public final class space/kscience/magix/server/RSocketMagixFlowPlugin : space/kscience/magix/api/MagixFlowPlugin { + public static final field Companion Lspace/kscience/magix/server/RSocketMagixFlowPlugin$Companion; + public fun ()V + public fun (Ljava/lang/String;ILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V + public synthetic fun (Ljava/lang/String;ILkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun start (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job; +} + +public final class space/kscience/magix/server/RSocketMagixFlowPlugin$Companion { + public final fun acceptor (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;)Lio/rsocket/kotlin/ConnectionAcceptor; +} + +public final class space/kscience/magix/server/ServerKt { + public static final fun startMagixServer (Lkotlinx/coroutines/CoroutineScope;[Lspace/kscience/magix/api/MagixFlowPlugin;II)Lio/ktor/server/engine/ApplicationEngine; + public static synthetic fun startMagixServer$default (Lkotlinx/coroutines/CoroutineScope;[Lspace/kscience/magix/api/MagixFlowPlugin;IIILjava/lang/Object;)Lio/ktor/server/engine/ApplicationEngine; +} + +public final class space/kscience/magix/server/SseEvent { + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lspace/kscience/magix/server/SseEvent; + public static synthetic fun copy$default (Lspace/kscience/magix/server/SseEvent;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lspace/kscience/magix/server/SseEvent; + public fun equals (Ljava/lang/Object;)Z + public final fun getData ()Ljava/lang/String; + public final fun getEvent ()Ljava/lang/String; + public final fun getId ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class space/kscience/magix/server/SseKt { + public static final fun respondSse (Lio/ktor/server/application/ApplicationCall;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun writeSseFlow (Lio/ktor/utils/io/ByteWriteChannel;Lkotlinx/coroutines/flow/Flow;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + diff --git a/magix/magix-storage/api/magix-storage.api b/magix/magix-storage/api/magix-storage.api new file mode 100644 index 0000000..4dfdc72 --- /dev/null +++ b/magix/magix-storage/api/magix-storage.api @@ -0,0 +1,183 @@ +public final class space/kscience/magix/storage/HistoryEndpointKt { + public static final fun launchHistory (Lspace/kscience/magix/api/MagixEndpoint;Lkotlinx/coroutines/CoroutineScope;Lspace/kscience/magix/storage/MagixHistory;Ljava/lang/String;ILkotlinx/serialization/json/JsonElement;Ljava/lang/String;)Lkotlinx/coroutines/Job; + public static synthetic fun launchHistory$default (Lspace/kscience/magix/api/MagixEndpoint;Lkotlinx/coroutines/CoroutineScope;Lspace/kscience/magix/storage/MagixHistory;Ljava/lang/String;ILkotlinx/serialization/json/JsonElement;Ljava/lang/String;ILjava/lang/Object;)Lkotlinx/coroutines/Job; +} + +public final class space/kscience/magix/storage/HistoryRequestPayload : space/kscience/magix/storage/MagixHistoryPayload { + public static final field Companion Lspace/kscience/magix/storage/HistoryRequestPayload$Companion; + public synthetic fun (ILspace/kscience/magix/api/MagixMessageFilter;Lspace/kscience/magix/storage/MagixPayloadFilter;Ljava/lang/String;Ljava/lang/Integer;Lkotlinx/serialization/internal/SerializationConstructorMarker;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Lspace/kscience/magix/api/MagixMessageFilter;Lspace/kscience/magix/storage/MagixPayloadFilter;Ljava/lang/String;Ljava/lang/Integer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Lspace/kscience/magix/api/MagixMessageFilter;Lspace/kscience/magix/storage/MagixPayloadFilter;Ljava/lang/String;Ljava/lang/Integer;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lspace/kscience/magix/api/MagixMessageFilter; + public final fun component2 ()Lspace/kscience/magix/storage/MagixPayloadFilter; + public final fun component3-G2i_Sr0 ()Ljava/lang/String; + public final fun component4 ()Ljava/lang/Integer; + public final fun copy-waRmP5I (Lspace/kscience/magix/api/MagixMessageFilter;Lspace/kscience/magix/storage/MagixPayloadFilter;Ljava/lang/String;Ljava/lang/Integer;)Lspace/kscience/magix/storage/HistoryRequestPayload; + public static synthetic fun copy-waRmP5I$default (Lspace/kscience/magix/storage/HistoryRequestPayload;Lspace/kscience/magix/api/MagixMessageFilter;Lspace/kscience/magix/storage/MagixPayloadFilter;Ljava/lang/String;Ljava/lang/Integer;ILjava/lang/Object;)Lspace/kscience/magix/storage/HistoryRequestPayload; + public fun equals (Ljava/lang/Object;)Z + public final fun getMagixFilter ()Lspace/kscience/magix/api/MagixMessageFilter; + public final fun getPageSize ()Ljava/lang/Integer; + public final fun getPayloadFilter ()Lspace/kscience/magix/storage/MagixPayloadFilter; + public final fun getUserFilter-G2i_Sr0 ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/magix/storage/HistoryRequestPayload;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/magix/storage/HistoryRequestPayload$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/magix/storage/HistoryRequestPayload$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/magix/storage/HistoryRequestPayload; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/magix/storage/HistoryRequestPayload;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/storage/HistoryRequestPayload$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/storage/HistoryResponsePayload : space/kscience/magix/storage/MagixHistoryPayload { + public static final field Companion Lspace/kscience/magix/storage/HistoryResponsePayload$Companion; + public synthetic fun (ILjava/util/List;IZLkotlinx/serialization/internal/SerializationConstructorMarker;)V + public fun (Ljava/util/List;IZ)V + public synthetic fun (Ljava/util/List;IZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/util/List; + public final fun component2 ()I + public final fun component3 ()Z + public final fun copy (Ljava/util/List;IZ)Lspace/kscience/magix/storage/HistoryResponsePayload; + public static synthetic fun copy$default (Lspace/kscience/magix/storage/HistoryResponsePayload;Ljava/util/List;IZILjava/lang/Object;)Lspace/kscience/magix/storage/HistoryResponsePayload; + public fun equals (Ljava/lang/Object;)Z + public final fun getLastPage ()Z + public final fun getMessages ()Ljava/util/List; + public final fun getPage ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public static final synthetic fun write$Self (Lspace/kscience/magix/storage/HistoryResponsePayload;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/magix/storage/HistoryResponsePayload$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/magix/storage/HistoryResponsePayload$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lspace/kscience/magix/storage/HistoryResponsePayload; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lspace/kscience/magix/storage/HistoryResponsePayload;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/storage/HistoryResponsePayload$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public abstract interface class space/kscience/magix/storage/MagixHistory { + public static final field Companion Lspace/kscience/magix/storage/MagixHistory$Companion; + public static final field HISTORY_PAYLOAD_FORMAT Ljava/lang/String; + public abstract fun useMessages-2Yl6TEQ (Lspace/kscience/magix/api/MagixMessageFilter;Lspace/kscience/magix/storage/MagixPayloadFilter;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun useMessages-2Yl6TEQ$default (Lspace/kscience/magix/storage/MagixHistory;Lspace/kscience/magix/api/MagixMessageFilter;Lspace/kscience/magix/storage/MagixPayloadFilter;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; +} + +public final class space/kscience/magix/storage/MagixHistory$Companion { + public static final field HISTORY_PAYLOAD_FORMAT Ljava/lang/String; + public final fun getMagixFormat ()Lspace/kscience/magix/api/MagixFormat; +} + +public final class space/kscience/magix/storage/MagixHistoryKt { + public static final fun filter (Lkotlin/sequences/Sequence;Lspace/kscience/magix/storage/MagixPayloadFilter;)Lkotlin/sequences/Sequence; + public static final fun test (Lspace/kscience/magix/storage/MagixPayloadFilter;Lkotlinx/serialization/json/JsonElement;)Z +} + +public abstract class space/kscience/magix/storage/MagixHistoryPayload { + public static final field Companion Lspace/kscience/magix/storage/MagixHistoryPayload$Companion; + public synthetic fun (ILkotlinx/serialization/internal/SerializationConstructorMarker;)V + public static final synthetic fun write$Self (Lspace/kscience/magix/storage/MagixHistoryPayload;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/magix/storage/MagixHistoryPayload$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public abstract class space/kscience/magix/storage/MagixPayloadFilter { + public static final field Companion Lspace/kscience/magix/storage/MagixPayloadFilter$Companion; + public synthetic fun (ILkotlinx/serialization/internal/SerializationConstructorMarker;)V + public static final synthetic fun write$Self (Lspace/kscience/magix/storage/MagixPayloadFilter;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V +} + +public final class space/kscience/magix/storage/MagixPayloadFilter$And : space/kscience/magix/storage/MagixPayloadFilter { + public fun (Lspace/kscience/magix/storage/MagixPayloadFilter;Lspace/kscience/magix/storage/MagixPayloadFilter;)V + public final fun getLeft ()Lspace/kscience/magix/storage/MagixPayloadFilter; + public final fun getRight ()Lspace/kscience/magix/storage/MagixPayloadFilter; +} + +public final class space/kscience/magix/storage/MagixPayloadFilter$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/storage/MagixPayloadFilter$DateTimeInRange : space/kscience/magix/storage/MagixPayloadFilter { + public fun (Ljava/lang/String;Lkotlinx/datetime/LocalDateTime;Lkotlinx/datetime/LocalDateTime;)V + public final fun getFrom ()Lkotlinx/datetime/LocalDateTime; + public final fun getPath ()Ljava/lang/String; + public final fun getTo ()Lkotlinx/datetime/LocalDateTime; +} + +public final class space/kscience/magix/storage/MagixPayloadFilter$Equals : space/kscience/magix/storage/MagixPayloadFilter { + public fun (Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;)V + public final fun getPath ()Ljava/lang/String; + public final fun getValue ()Lkotlinx/serialization/json/JsonElement; +} + +public final class space/kscience/magix/storage/MagixPayloadFilter$Not : space/kscience/magix/storage/MagixPayloadFilter { + public fun (Lspace/kscience/magix/storage/MagixPayloadFilter;)V + public final fun getArgument ()Lspace/kscience/magix/storage/MagixPayloadFilter; +} + +public final class space/kscience/magix/storage/MagixPayloadFilter$NumberInRange : space/kscience/magix/storage/MagixPayloadFilter { + public fun (Ljava/lang/String;Ljava/lang/Number;Ljava/lang/Number;)V + public final fun getFrom ()Ljava/lang/Number; + public final fun getPath ()Ljava/lang/String; + public final fun getTo ()Ljava/lang/Number; +} + +public final class space/kscience/magix/storage/MagixPayloadFilter$Or : space/kscience/magix/storage/MagixPayloadFilter { + public fun (Lspace/kscience/magix/storage/MagixPayloadFilter;Lspace/kscience/magix/storage/MagixPayloadFilter;)V + public final fun getLeft ()Lspace/kscience/magix/storage/MagixPayloadFilter; + public final fun getRight ()Lspace/kscience/magix/storage/MagixPayloadFilter; +} + +public final class space/kscience/magix/storage/MagixUsernameFilter { + public static final field Companion Lspace/kscience/magix/storage/MagixUsernameFilter$Companion; + public static final synthetic fun box-impl (Ljava/lang/String;)Lspace/kscience/magix/storage/MagixUsernameFilter; + public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String; + public fun equals (Ljava/lang/Object;)Z + public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z + public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z + public final fun getUserName ()Ljava/lang/String; + public fun hashCode ()I + public static fun hashCode-impl (Ljava/lang/String;)I + public fun toString ()Ljava/lang/String; + public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String; + public final synthetic fun unbox-impl ()Ljava/lang/String; +} + +public final class space/kscience/magix/storage/MagixUsernameFilter$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lspace/kscience/magix/storage/MagixUsernameFilter$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun deserialize-OjVmumU (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/String; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun serialize-dWVLUXE (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/String;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class space/kscience/magix/storage/MagixUsernameFilter$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public abstract interface class space/kscience/magix/storage/WriteableMagixHistory : space/kscience/magix/storage/MagixHistory { + public abstract fun send (Lspace/kscience/magix/api/MagixMessage;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + diff --git a/magix/magix-zmq/api/magix-zmq.api b/magix/magix-zmq/api/magix-zmq.api new file mode 100644 index 0000000..428d464 --- /dev/null +++ b/magix/magix-zmq/api/magix-zmq.api @@ -0,0 +1,23 @@ +public final class space/kscince/magix/zmq/ZmqMagixEndpoint : java/lang/AutoCloseable, space/kscience/magix/api/MagixEndpoint { + public fun (Ljava/lang/String;Ljava/lang/String;IILkotlin/coroutines/CoroutineContext;Lorg/zeromq/ZContext;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;IILkotlin/coroutines/CoroutineContext;Lorg/zeromq/ZContext;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun broadcast (Lspace/kscience/magix/api/MagixMessage;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun close ()V + public fun subscribe (Lspace/kscience/magix/api/MagixMessageFilter;)Lkotlinx/coroutines/flow/Flow; +} + +public final class space/kscince/magix/zmq/ZmqMagixEndpointKt { + public static final fun zmq (Lspace/kscience/magix/api/MagixEndpoint$Companion;Ljava/lang/String;Ljava/lang/String;IILkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun zmq$default (Lspace/kscience/magix/api/MagixEndpoint$Companion;Ljava/lang/String;Ljava/lang/String;IILkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; +} + +public final class space/kscince/magix/zmq/ZmqMagixFlowPlugin : space/kscience/magix/api/MagixFlowPlugin { + public fun ()V + public fun (Ljava/lang/String;IILorg/zeromq/ZContext;)V + public synthetic fun (Ljava/lang/String;IILorg/zeromq/ZContext;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getLocalHost ()Ljava/lang/String; + public final fun getZmqPubSocketPort ()I + public final fun getZmqPullSocketPort ()I + public fun start (Lkotlinx/coroutines/CoroutineScope;Lkotlinx/coroutines/flow/Flow;Lkotlin/jvm/functions/Function2;)Lkotlinx/coroutines/Job; +} +