Merge pull request #9 from SciProgCentre/dev

0.2.0
This commit is contained in:
SPC-code 2023-08-23 16:27:13 +03:00 committed by GitHub
commit 5b655a9354
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
145 changed files with 5210 additions and 1086 deletions

32
CHANGELOG.md Normal file
View File

@ -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

View File

@ -43,7 +43,7 @@ Example view of a demo:
### [controls-core](controls-core) ### [controls-core](controls-core)
> > Core interfaces for building a device server
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
> >
@ -51,136 +51,151 @@ 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) > - [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. > - [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. > - [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-ktor-tcp](controls-ktor-tcp) ### [controls-magix](controls-magix)
> > Magix service for binding controls devices (both as RPC client and server)
> >
> **Maturity**: EXPERIMENTAL > **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-magix-client](controls-magix-client)
>
>
> **Maturity**: EXPERIMENTAL
### [controls-modbus](controls-modbus) ### [controls-modbus](controls-modbus)
> > A plugin for Controls-kt device server on top of modbus-rtu/modbus-tcp protocols
> >
> **Maturity**: EXPERIMENTAL > **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) ### [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 > **Maturity**: EXPERIMENTAL
### [controls-serial](controls-serial) ### [controls-ports-ktor](controls-ports-ktor)
> Implementation of byte ports on top os ktor-io asynchronous API
> >
> **Maturity**: PROTOTYPE
### [controls-serial](controls-serial)
> Implementation of direct serial port communication with JSerialComm
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [controls-server](controls-server) ### [controls-server](controls-server)
> A combined Magix event loop server with web server for visualization.
> >
> > **Maturity**: PROTOTYPE
> **Maturity**: EXPERIMENTAL
### [controls-storage](controls-storage) ### [controls-storage](controls-storage)
> > An API for stand-alone Controls-kt device or a hub.
> >
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
### [demo](demo) ### [demo](demo)
> >
>
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [magix](magix) ### [magix](magix)
> >
>
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [controls-storage/controls-xodus](controls-storage/controls-xodus) ### [controls-storage/controls-xodus](controls-storage/controls-xodus)
> > An implementation of controls-storage on top of JetBrains Xodus.
> >
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
### [demo/all-things](demo/all-things) ### [demo/all-things](demo/all-things)
> >
>
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [demo/car](demo/car) ### [demo/car](demo/car)
> >
>
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [demo/echo](demo/echo) ### [demo/echo](demo/echo)
> >
>
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [demo/magix-demo](demo/magix-demo) ### [demo/magix-demo](demo/magix-demo)
> >
>
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [demo/many-devices](demo/many-devices) ### [demo/many-devices](demo/many-devices)
> >
>
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [demo/mks-pdr900](demo/mks-pdr900) ### [demo/mks-pdr900](demo/mks-pdr900)
> >
>
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [demo/motors](demo/motors) ### [demo/motors](demo/motors)
> >
>
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [magix/magix-api](magix/magix-api) ### [magix/magix-api](magix/magix-api)
> > A kotlin API for magix standard and some zero-dependency magix services
> >
> **Maturity**: EXPERIMENTAL > **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 > **Maturity**: EXPERIMENTAL
### [magix/magix-mqtt](magix/magix-mqtt) ### [magix/magix-mqtt](magix/magix-mqtt)
> > MQTT client magix endpoint
> >
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
### [magix/magix-rabbit](magix/magix-rabbit) ### [magix/magix-rabbit](magix/magix-rabbit)
> > RabbitMQ client magix endpoint
> >
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
### [magix/magix-rsocket](magix/magix-rsocket) ### [magix/magix-rsocket](magix/magix-rsocket)
> > Magix endpoint (client) based on RSocket
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [magix/magix-server](magix/magix-server) ### [magix/magix-server](magix/magix-server)
> > A magix event loop implementation in Kotlin. Includes HTTP/SSE and RSocket routes.
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [magix/magix-storage](magix/magix-storage) ### [magix/magix-storage](magix/magix-storage)
> Magix history database API
> >
> > **Maturity**: PROTOTYPE
> **Maturity**: EXPERIMENTAL
### [magix/magix-zmq](magix/magix-zmq) ### [magix/magix-zmq](magix/magix-zmq)
> > ZMQ client endpoint for Magix
> >
> **Maturity**: EXPERIMENTAL > **Maturity**: EXPERIMENTAL
### [magix/magix-storage/magix-storage-xodus](magix/magix-storage/magix-storage-xodus) ### [magix/magix-storage/magix-storage-xodus](magix/magix-storage/magix-storage-xodus)
> >
>
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE

View File

@ -6,14 +6,14 @@ plugins {
id("space.kscience.gradle.project") 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 ktorVersion: String by extra(space.kscience.gradle.KScienceVersions.ktorVersion)
val rsocketVersion by extra("0.15.4") val rsocketVersion by extra("0.15.4")
val xodusVersion by extra("2.0.1") val xodusVersion by extra("2.0.1")
allprojects { allprojects {
group = "space.kscience" group = "space.kscience"
version = "0.2.0-dev-1" version = "0.2.0"
repositories{ repositories{
maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev") maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
} }
@ -29,10 +29,9 @@ ksciencePublish {
if (isInDevelopment) { if (isInDevelopment) {
"https://maven.pkg.jetbrains.space/spc/p/sci/dev" "https://maven.pkg.jetbrains.space/spc/p/sci/dev"
} else { } 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") readme.readmeTemplate = file("docs/templates/README-TEMPLATE.md")

33
controls-core/README.md Normal file
View File

@ -0,0 +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`.
**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")
}
```

View File

@ -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 <init> (ILjava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (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 <init> (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 <init> (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 <init> (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 <init> (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 <init> (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 <init> (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 <init> (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 <init> (Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V
public synthetic fun <init> (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 <init> (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 <init> (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 <init> (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 <init> (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 <init> (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 <init> (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 <init> (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 <init> (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 <init> (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 <init> (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 <init> ()V
public synthetic fun <init> (ILspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V
public synthetic fun <init> (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 <init> (ILspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V
public synthetic fun <init> (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 <init> (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 <init> (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 <init> (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 <init> (ILjava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ZZLkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lspace/kscience/dataforge/meta/descriptors/MetaDescriptor;ZZ)V
public synthetic fun <init> (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 <init> (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 <init> (Ljava/lang/String;Lspace/kscience/dataforge/names/Name;Lspace/kscience/dataforge/names/Name;Ljava/lang/String;Lkotlinx/datetime/Instant;)V
public synthetic fun <init> (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 <init> (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 <init> (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 <init> (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 <init> ()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 <init> (Lspace/kscience/dataforge/context/Context;Lkotlin/coroutines/CoroutineContext;)V
public synthetic fun <init> (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 <init> (Lspace/kscience/dataforge/context/Context;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function1;)V
public synthetic fun <init> (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 <init> ()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 <init> (Lspace/kscience/dataforge/context/Context;Lkotlin/jvm/functions/Function1;)V
public synthetic fun <init> (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 <init> ()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 <init> (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 <init> (Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)V
public synthetic fun <init> (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 <init> (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/dataforge/context/Context;Lspace/kscience/dataforge/meta/Meta;)V
public synthetic fun <init> (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 <init> ()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;
}

View File

@ -1,8 +1,14 @@
import space.kscience.gradle.Maturity
plugins { plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
`maven-publish` `maven-publish`
} }
description = """
Core interfaces for building a device server
""".trimIndent()
val dataforgeVersion: String by rootProject.extra val dataforgeVersion: String by rootProject.extra
kscience { kscience {
@ -22,25 +28,41 @@ kscience {
readme{ readme{
maturity = Maturity.EXPERIMENTAL
feature("device", ref = "src/commonMain/kotlin/space/kscience/controls/api/Device.kt"){ feature("device", ref = "src/commonMain/kotlin/space/kscience/controls/api/Device.kt"){
""" """
Device API with subscription (asynchronous and pseudo-synchronous properties) Device API with subscription (asynchronous and pseudo-synchronous properties)
""".trimIndent() """.trimIndent()
} }
}
readme{
feature("deviceMessage", ref = "src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt"){ feature("deviceMessage", ref = "src/commonMain/kotlin/space/kscience/controls/api/DeviceMessage.kt"){
""" """
Specification for messages used to communicate between Controls-kt devices. Specification for messages used to communicate between Controls-kt devices.
""".trimIndent() """.trimIndent()
} }
}
readme{
feature("deviceHub", ref = "src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt"){ feature("deviceHub", ref = "src/commonMain/kotlin/space/kscience/controls/api/DeviceHub.kt"){
""" """
Grouping of devices into local tree-like hubs. Grouping of devices into local tree-like hubs.
""".trimIndent() """.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()
}
} }

View File

@ -9,10 +9,21 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import space.kscience.controls.api.Device.Companion.DEVICE_TARGET import space.kscience.controls.api.Device.Companion.DEVICE_TARGET
import space.kscience.dataforge.context.ContextAware 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.meta.Meta
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.misc.Type import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.Name 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. * General interface describing a managed Device.
@ -79,12 +90,16 @@ public interface Device : AutoCloseable, ContextAware, CoroutineScope {
public suspend fun open(): Unit = Unit 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() { override fun close() {
logger.info { "Device $this is closed" }
cancel("The device is closed") cancel("The device is closed")
} }
@DFExperimental
public val lifecycleState: DeviceLifecycleState
public companion object { public companion object {
public const val DEVICE_TARGET: String = "device" public const val DEVICE_TARGET: String = "device"
} }

View File

@ -14,9 +14,8 @@ public interface DeviceHub : Provider {
override val defaultChainTarget: String get() = Device.DEVICE_TARGET override val defaultChainTarget: String get() = Device.DEVICE_TARGET
override fun content(target: String): Map<Name, Any> { override fun content(target: String): Map<Name, Any> = if (target == Device.DEVICE_TARGET) {
if (target == Device.DEVICE_TARGET) { buildMap {
return buildMap {
fun putAll(prefix: Name, hub: DeviceHub) { fun putAll(prefix: Name, hub: DeviceHub) {
hub.devices.forEach { hub.devices.forEach {
put(prefix + it.key, it.value) put(prefix + it.key, it.value)
@ -32,8 +31,7 @@ public interface DeviceHub : Provider {
} }
} }
} else { } else {
throw IllegalArgumentException("Target $target is not supported for $this") emptyMap()
}
} }
public companion object public companion object

View File

@ -1,7 +1,11 @@
@file:OptIn(ExperimentalSerializationApi::class)
package space.kscience.controls.api package space.kscience.controls.api
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant import kotlinx.datetime.Instant
import kotlinx.serialization.EncodeDefault
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
@ -55,9 +59,9 @@ public data class PropertyChangedMessage(
override val sourceDevice: Name = Name.EMPTY, override val sourceDevice: Name = Name.EMPTY,
override val targetDevice: Name? = null, override val targetDevice: Name? = null,
override val comment: String? = null, override val comment: String? = null,
override val time: Instant? = Clock.System.now() @EncodeDefault override val time: Instant? = Clock.System.now(),
) : DeviceMessage(){ ) : DeviceMessage() {
override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = block(sourceDevice)) 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 sourceDevice: Name? = null,
override val targetDevice: Name, override val targetDevice: Name,
override val comment: String? = null, override val comment: String? = null,
override val time: Instant? = Clock.System.now() @EncodeDefault override val time: Instant? = Clock.System.now(),
) : DeviceMessage(){ ) : DeviceMessage() {
override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) 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 sourceDevice: Name? = null,
override val targetDevice: Name, override val targetDevice: Name,
override val comment: String? = null, override val comment: String? = null,
override val time: Instant? = Clock.System.now() @EncodeDefault override val time: Instant? = Clock.System.now(),
) : DeviceMessage(){ ) : DeviceMessage() {
override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) 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 sourceDevice: Name? = null,
override val targetDevice: Name, override val targetDevice: Name,
override val comment: String? = null, override val comment: String? = null,
override val time: Instant? = Clock.System.now() @EncodeDefault override val time: Instant? = Clock.System.now(),
) : DeviceMessage(){ ) : DeviceMessage() {
override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) 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 sourceDevice: Name,
override val targetDevice: Name? = null, override val targetDevice: Name? = null,
override val comment: String? = null, override val comment: String? = null,
override val time: Instant? = Clock.System.now() @EncodeDefault override val time: Instant? = Clock.System.now(),
) : DeviceMessage(){ ) : DeviceMessage() {
override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = block(sourceDevice)) 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 sourceDevice: Name? = null,
override val targetDevice: Name, override val targetDevice: Name,
override val comment: String? = null, override val comment: String? = null,
override val time: Instant? = Clock.System.now() @EncodeDefault override val time: Instant? = Clock.System.now(),
) : DeviceMessage(){ ) : DeviceMessage() {
override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) 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 sourceDevice: Name,
override val targetDevice: Name? = null, override val targetDevice: Name? = null,
override val comment: String? = null, override val comment: String? = null,
override val time: Instant? = Clock.System.now() @EncodeDefault override val time: Instant? = Clock.System.now(),
) : DeviceMessage(){ ) : DeviceMessage() {
override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = block(sourceDevice)) 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 sourceDevice: Name,
override val targetDevice: Name? = null, override val targetDevice: Name? = null,
override val comment: String? = null, override val comment: String? = null,
override val time: Instant? = Clock.System.now() @EncodeDefault override val time: Instant? = Clock.System.now(),
) : DeviceMessage(){ ) : DeviceMessage() {
override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = block(sourceDevice)) 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 sourceDevice: Name? = null,
override val targetDevice: Name? = null, override val targetDevice: Name? = null,
override val comment: String? = null, override val comment: String? = null,
override val time: Instant? = Clock.System.now() @EncodeDefault override val time: Instant? = Clock.System.now(),
) : DeviceMessage(){ ) : DeviceMessage() {
override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) 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 sourceDevice: Name? = null,
override val targetDevice: Name? = null, override val targetDevice: Name? = null,
override val comment: String? = null, override val comment: String? = null,
override val time: Instant? = Clock.System.now() @EncodeDefault override val time: Instant? = Clock.System.now(),
) : DeviceMessage(){ ) : DeviceMessage() {
override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = sourceDevice?.let(block)) 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 sourceDevice: Name,
override val targetDevice: Name? = null, override val targetDevice: Name? = null,
override val comment: String? = null, override val comment: String? = null,
override val time: Instant? = Clock.System.now() @EncodeDefault override val time: Instant? = Clock.System.now(),
) : DeviceMessage(){ ) : DeviceMessage() {
override fun changeSource(block: (Name) -> Name):DeviceMessage = copy(sourceDevice = block(sourceDevice)) override fun changeSource(block: (Name) -> Name): DeviceMessage = copy(sourceDevice = block(sourceDevice))
} }

View File

@ -37,12 +37,7 @@ public class DeviceManager : AbstractPlugin(), DeviceHub {
} }
} }
public fun <D : Device> DeviceManager.install(name: String, device: D): D {
/**
* Register and start a device built by [factory] with current [Context] and [meta].
*/
public fun <D : Device> DeviceManager.install(name: String, factory: Factory<D>, meta: Meta = Meta.EMPTY): D {
val device = factory(meta, context)
registerDevice(NameToken(name), device) registerDevice(NameToken(name), device)
device.launch { device.launch {
device.open() device.open()
@ -50,6 +45,13 @@ public fun <D : Device> DeviceManager.install(name: String, factory: Factory<D>,
return device return device
} }
/**
* Register and start a device built by [factory] with current [Context] and [meta].
*/
public fun <D : Device> DeviceManager.install(name: String, factory: Factory<D>, meta: Meta = Meta.EMPTY): D =
install(name, factory(meta, context))
/** /**
* A delegate that initializes device on the first use * A delegate that initializes device on the first use
*/ */

View File

@ -1,18 +1,18 @@
package space.kscience.controls.misc package space.kscience.controls.misc
import kotlinx.datetime.Instant
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.long import space.kscience.dataforge.meta.long
import java.time.Instant
// TODO move to core // TODO move to core
public fun Instant.toMeta(): Meta = Meta { public fun Instant.toMeta(): Meta = Meta {
"seconds" put epochSecond "seconds" put epochSeconds
"nanos" put nano "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("seconds")?.long ?: 0L,
get("nanos")?.long ?: 0L, get("nanos")?.long ?: 0L,
) )

View File

@ -18,10 +18,10 @@ public interface Port : ContextAware, Socket<ByteArray>
* A specialized factory for [Port] * A specialized factory for [Port]
*/ */
@Type(PortFactory.TYPE) @Type(PortFactory.TYPE)
public interface PortFactory: Factory<Port>{ public interface PortFactory : Factory<Port> {
public val type: String public val type: String
public companion object{ public companion object {
public const val TYPE: String = "controls.port" public const val TYPE: String = "controls.port"
} }
} }
@ -53,12 +53,10 @@ public abstract class AbstractPort(
/** /**
* Internal method to receive data synchronously * Internal method to receive data synchronously
*/ */
protected fun receive(data: ByteArray) { protected suspend fun receive(data: ByteArray) {
scope.launch {
logger.debug { "${this@AbstractPort} RECEIVED: ${data.decodeToString()}" } logger.debug { "${this@AbstractPort} RECEIVED: ${data.decodeToString()}" }
incoming.send(data) incoming.send(data)
} }
}
private val sendJob = scope.launch { private val sendJob = scope.launch {
for (data in outgoing) { for (data in outgoing) {
@ -82,7 +80,7 @@ public abstract class AbstractPort(
/** /**
* Raw flow of incoming data chunks. The chunks are not guaranteed to be complete phrases. * 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. * 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<ByteArray> = incoming.receiveAsFlow() override fun receiving(): Flow<ByteArray> = incoming.receiveAsFlow()

View File

@ -48,3 +48,8 @@ public fun Flow<ByteArray>.withStringDelimiter(delimiter: String): Flow<String>
* A flow of delimited phrases * A flow of delimited phrases
*/ */
public fun Port.delimitedIncoming(delimiter: ByteArray): Flow<ByteArray> = receiving().withDelimiter(delimiter) public fun Port.delimitedIncoming(delimiter: ByteArray): Flow<ByteArray> = receiving().withDelimiter(delimiter)
/**
* A flow of delimited phrases with string content
*/
public fun Port.stringsDelimitedIncoming(delimiter: String): Flow<String> = receiving().withStringDelimiter(delimiter)

View File

@ -1,15 +1,16 @@
package space.kscience.controls.spec package space.kscience.controls.spec
import kotlinx.coroutines.Job import kotlinx.coroutines.*
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import space.kscience.controls.api.* import space.kscience.controls.api.*
import space.kscience.dataforge.context.Context 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.meta.Meta
import space.kscience.dataforge.misc.DFExperimental
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@ -25,9 +26,9 @@ private suspend fun <D : Device, T> DevicePropertySpec<D, T>.readMeta(device: D)
private suspend fun <D : Device, I, O> DeviceActionSpec<D, I, O>.executeWithMeta( private suspend fun <D : Device, I, O> DeviceActionSpec<D, I, O>.executeWithMeta(
device: D, device: D,
item: Meta?, item: Meta,
): 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) val res = execute(device, arg)
return res?.let { outputConverter.objectToMeta(res) } return res?.let { outputConverter.objectToMeta(res) }
} }
@ -37,7 +38,7 @@ private suspend fun <D : Device, I, O> DeviceActionSpec<D, I, O>.executeWithMeta
* A base abstractions for [Device], introducing specifications for properties * A base abstractions for [Device], introducing specifications for properties
*/ */
public abstract class DeviceBase<D : Device>( public abstract class DeviceBase<D : Device>(
override val context: Context = Global, final override val context: Context,
override val meta: Meta = Meta.EMPTY, override val meta: Meta = Meta.EMPTY,
) : Device { ) : Device {
@ -58,8 +59,15 @@ public abstract class DeviceBase<D : Device>(
get() = actions.values.map { it.descriptor } get() = actions.values.map { it.descriptor }
override val coroutineContext: CoroutineContext by lazy { 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 * Logical state store
@ -146,8 +154,26 @@ public abstract class DeviceBase<D : Device>(
override suspend fun execute(actionName: String, argument: Meta?): Meta? { override suspend fun execute(actionName: String, argument: Meta?): Meta? {
val spec = actions[actionName] ?: error("Action with name $actionName not found") val spec = actions[actionName] ?: error("Action with name $actionName not found")
return spec.executeWithMeta(self, argument) 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
} }

View File

@ -2,7 +2,6 @@ package space.kscience.controls.spec
import space.kscience.controls.api.Device import space.kscience.controls.api.Device
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.Global
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
/** /**
@ -11,7 +10,7 @@ import space.kscience.dataforge.meta.Meta
*/ */
public open class DeviceBySpec<D : Device>( public open class DeviceBySpec<D : Device>(
public val spec: DeviceSpec<in D>, public val spec: DeviceSpec<in D>,
context: Context = Global, context: Context,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
) : DeviceBase<D>(context, meta) { ) : DeviceBase<D>(context, meta) {
override val properties: Map<String, DevicePropertySpec<D, *>> get() = spec.properties override val properties: Map<String, DevicePropertySpec<D, *>> get() = spec.properties
@ -26,4 +25,6 @@ public open class DeviceBySpec<D : Device>(
self.onClose() self.onClose()
super.close() super.close()
} }
override fun toString(): String = "Device(spec=$spec)"
} }

View File

@ -2,10 +2,7 @@ package space.kscience.controls.spec
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.*
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import space.kscience.controls.api.ActionDescriptor import space.kscience.controls.api.ActionDescriptor
import space.kscience.controls.api.Device import space.kscience.controls.api.Device
@ -69,7 +66,7 @@ public interface DeviceActionSpec<in D : Device, I, O> {
/** /**
* Execute action on a device * Execute action on a device
*/ */
public suspend fun execute(device: D, input: I?): O? public suspend fun execute(device: D, input: I): O
} }
/** /**
@ -105,19 +102,50 @@ public operator fun <T, D : Device> D.set(propertySpec: WritableDevicePropertySp
write(propertySpec, value) write(propertySpec, value)
} }
/**
* A type safe flow of property changes for given property
*/
public fun <D : Device, T> D.propertyFlow(spec: DevicePropertySpec<D, T>): Flow<T> = messageFlow
.filterIsInstance<PropertyChangedMessage>()
.filter { it.property == spec.name }
.mapNotNull { spec.converter.metaToObject(it.value) }
/** /**
* A type safe property change listener. Uses the device [CoroutineScope]. * A type safe property change listener. Uses the device [CoroutineScope].
*/ */
public fun <D : Device, T> Device.onPropertyChange( public fun <D : Device, T> D.onPropertyChange(
spec: DevicePropertySpec<D, T>, spec: DevicePropertySpec<D, T>,
callback: suspend PropertyChangedMessage.(T?) -> Unit, callback: suspend PropertyChangedMessage.(T) -> Unit,
): Job = messageFlow ): Job = messageFlow
.filterIsInstance<PropertyChangedMessage>() .filterIsInstance<PropertyChangedMessage>()
.filter { it.property == spec.name } .filter { it.property == spec.name }
.onEach { change -> .onEach { change ->
change.callback(spec.converter.metaToObject(change.value)) val newValue = spec.converter.metaToObject(change.value)
if (newValue != null) {
change.callback(newValue)
}
}.launchIn(this) }.launchIn(this)
/**
* Call [callback] on initial property value and each value change
*/
public fun <D : Device, T> D.useProperty(
spec: DevicePropertySpec<D, T>,
callback: suspend (T) -> Unit,
): Job = launch {
callback(read(spec))
messageFlow
.filterIsInstance<PropertyChangedMessage>()
.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 * Reset the logical state of a property
*/ */
@ -128,5 +156,8 @@ public suspend fun <D : Device> D.invalidate(propertySpec: DevicePropertySpec<D,
/** /**
* Execute the action with name according to [actionSpec] * Execute the action with name according to [actionSpec]
*/ */
public suspend fun <I, O, D : Device> D.execute(actionSpec: DeviceActionSpec<D, I, O>, input: I? = null): O? = public suspend fun <I, O, D : Device> D.execute(actionSpec: DeviceActionSpec<D, I, O>, input: I): O =
actionSpec.execute(this, input) actionSpec.execute(this, input)
public suspend fun <O, D : Device> D.execute(actionSpec: DeviceActionSpec<D, Unit, O>): O =
actionSpec.execute(this, Unit)

View File

@ -12,6 +12,13 @@ import kotlin.reflect.KMutableProperty1
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.reflect.KProperty1 import kotlin.reflect.KProperty1
public object UnitMetaConverter: MetaConverter<Unit>{
override fun metaToObject(meta: Meta): Unit = Unit
override fun objectToMeta(obj: Unit): Meta = Meta.EMPTY
}
public val MetaConverter.Companion.unit: MetaConverter<Unit> get() = UnitMetaConverter
@OptIn(InternalDeviceAPI::class) @OptIn(InternalDeviceAPI::class)
public abstract class DeviceSpec<D : Device> { public abstract class DeviceSpec<D : Device> {
@ -37,25 +44,6 @@ public abstract class DeviceSpec<D : Device> {
return deviceProperty return deviceProperty
} }
// public fun <T> registerProperty(
// converter: MetaConverter<T>,
// readOnlyProperty: KProperty1<D, T>,
// descriptorBuilder: PropertyDescriptor.() -> Unit = {},
// ): DevicePropertySpec<D, T> {
// val deviceProperty = object : DevicePropertySpec<D, T> {
//
// override val descriptor: PropertyDescriptor = PropertyDescriptor(readOnlyProperty.name)
// .apply(descriptorBuilder)
//
// override val converter: MetaConverter<T> = converter
//
// override suspend fun read(device: D): T = withContext(device.coroutineContext) {
// readOnlyProperty.get(device)
// }
// }
// return registerProperty(deviceProperty)
// }
public fun <T> property( public fun <T> property(
converter: MetaConverter<T>, converter: MetaConverter<T>,
readOnlyProperty: KProperty1<D, T>, readOnlyProperty: KProperty1<D, T>,
@ -89,7 +77,7 @@ public abstract class DeviceSpec<D : Device> {
val deviceProperty = object : WritableDevicePropertySpec<D, T> { val deviceProperty = object : WritableDevicePropertySpec<D, T> {
override val descriptor: PropertyDescriptor = PropertyDescriptor(property.name).apply { override val descriptor: PropertyDescriptor = PropertyDescriptor(property.name).apply {
//TODO add type from converter //TODO add the type from converter
writable = true writable = true
}.apply(descriptorBuilder) }.apply(descriptorBuilder)
@ -165,7 +153,7 @@ public abstract class DeviceSpec<D : Device> {
outputConverter: MetaConverter<O>, outputConverter: MetaConverter<O>,
descriptorBuilder: ActionDescriptor.() -> Unit = {}, descriptorBuilder: ActionDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
execute: suspend D.(I?) -> O?, execute: suspend D.(I) -> O,
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, I, O>>> = ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, I, O>>> =
PropertyDelegateProvider { _: DeviceSpec<D>, property -> PropertyDelegateProvider { _: DeviceSpec<D>, property ->
val actionName = name ?: property.name val actionName = name ?: property.name
@ -175,7 +163,7 @@ public abstract class DeviceSpec<D : Device> {
override val inputConverter: MetaConverter<I> = inputConverter override val inputConverter: MetaConverter<I> = inputConverter
override val outputConverter: MetaConverter<O> = outputConverter override val outputConverter: MetaConverter<O> = 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) device.execute(input)
} }
} }
@ -191,7 +179,7 @@ public abstract class DeviceSpec<D : Device> {
public fun metaAction( public fun metaAction(
descriptorBuilder: ActionDescriptor.() -> Unit = {}, descriptorBuilder: ActionDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
execute: suspend D.(Meta?) -> Meta?, execute: suspend D.(Meta) -> Meta,
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, Meta, Meta>>> = ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, Meta, Meta>>> =
action( action(
MetaConverter.Companion.meta, MetaConverter.Companion.meta,
@ -209,15 +197,14 @@ public abstract class DeviceSpec<D : Device> {
descriptorBuilder: ActionDescriptor.() -> Unit = {}, descriptorBuilder: ActionDescriptor.() -> Unit = {},
name: String? = null, name: String? = null,
execute: suspend D.() -> Unit, execute: suspend D.() -> Unit,
): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, Meta, Meta>>> = ): PropertyDelegateProvider<DeviceSpec<D>, ReadOnlyProperty<DeviceSpec<D>, DeviceActionSpec<D, Unit, Unit>>> =
action( action(
MetaConverter.Companion.meta, MetaConverter.Companion.unit,
MetaConverter.Companion.meta, MetaConverter.Companion.unit,
descriptorBuilder, descriptorBuilder,
name name
) { ) {
execute() execute()
null
} }
} }

View File

@ -0,0 +1,133 @@
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
import java.nio.ByteBuffer
import java.nio.channels.ByteChannel
import java.nio.channels.DatagramChannel
import java.nio.channels.SocketChannel
import kotlin.coroutines.CoroutineContext
public fun ByteBuffer.toArray(limit: Int = limit()): ByteArray {
rewind()
val response = ByteArray(limit)
get(response)
rewind()
return response
}
/**
* A port based on nio [ByteChannel]
*/
public class ChannelPort(
context: Context,
coroutineContext: CoroutineContext = context.coroutineContext,
channelBuilder: suspend () -> ByteChannel,
) : AbstractPort(context, coroutineContext), AutoCloseable {
private val futureChannel: Deferred<ByteChannel> = this.scope.async(Dispatchers.IO) {
channelBuilder()
}
/**
* A handler to await port connection
*/
public val startJob: Job get() = futureChannel
private val listenerJob = this.scope.launch(Dispatchers.IO) {
val channel = futureChannel.await()
val buffer = ByteBuffer.allocate(1024)
while (isActive) {
try {
val num = channel.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)
}
}
}
override suspend fun write(data: ByteArray): Unit = withContext(Dispatchers.IO) {
futureChannel.await().write(ByteBuffer.wrap(data))
}
@OptIn(ExperimentalCoroutinesApi::class)
override fun close() {
listenerJob.cancel()
if (futureChannel.isCompleted) {
futureChannel.getCompleted().close()
} else {
futureChannel.cancel()
}
super.close()
}
}
/**
* A [PortFactory] for TCP connections
*/
public object TcpPort : PortFactory {
override val type: String = "tcp"
public fun open(
context: Context,
host: String,
port: Int,
coroutineContext: CoroutineContext = context.coroutineContext,
): ChannelPort = ChannelPort(context, coroutineContext) {
SocketChannel.open(InetSocketAddress(host, port))
}
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"
/**
* 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,
remoteHost: String,
remotePort: Int,
localPort: Int? = null,
localHost: String = "localhost",
coroutineContext: CoroutineContext = context.coroutineContext,
): ChannelPort = ChannelPort(context, coroutineContext) {
DatagramChannel.open().apply {
//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))
context.logger.info { "Connected to UDP $remotePort on $remoteHost" }
}
}
override fun build(context: Context, meta: Meta): ChannelPort {
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")
}
}

View File

@ -6,21 +6,29 @@ import space.kscience.dataforge.context.PluginFactory
import space.kscience.dataforge.context.PluginTag import space.kscience.dataforge.context.PluginTag
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name 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 val tag: PluginTag get() = Companion.tag
override fun content(target: String): Map<Name, Any> = when(target){ override fun content(target: String): Map<Name, Any> = when(target){
PortFactory.TYPE -> mapOf(Name.EMPTY to TcpPort) PortFactory.TYPE -> mapOf(
TcpPort.type.parseAsName() to TcpPort,
UdpPort.type.parseAsName() to UdpPort
)
else -> emptyMap() else -> emptyMap()
} }
public companion object : PluginFactory<TcpPortPlugin> { public companion object : PluginFactory<JvmPortsPlugin> {
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()
} }

View File

@ -1,95 +0,0 @@
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.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 java.net.InetSocketAddress
import java.nio.ByteBuffer
import java.nio.channels.SocketChannel
import kotlin.coroutines.CoroutineContext
internal fun ByteBuffer.readArray(limit: Int = limit()): ByteArray {
rewind()
val response = ByteArray(limit)
get(response)
rewind()
return response
}
public class TcpPort private constructor(
context: Context,
public val host: String,
public val port: Int,
coroutineContext: CoroutineContext = context.coroutineContext,
) : AbstractPort(context, coroutineContext), AutoCloseable {
override fun toString(): String = "port[tcp:$host:$port]"
private val futureChannel: Deferred<SocketChannel> = this.scope.async(Dispatchers.IO) {
SocketChannel.open(InetSocketAddress(host, port)).apply {
configureBlocking(false)
}
}
/**
* A handler to await port connection
*/
public val startJob: Job get() = futureChannel
private val listenerJob = this.scope.launch(Dispatchers.IO) {
val channel = futureChannel.await()
val buffer = ByteBuffer.allocate(1024)
while (isActive) {
try {
val num = channel.read(buffer)
if (num > 0) {
receive(buffer.readArray(num))
}
if (num < 0) cancel("The input channel is exhausted")
} catch (ex: Exception) {
logger.error(ex) { "Channel read error" }
delay(1000)
}
}
}
override suspend fun write(data: ByteArray): Unit = withContext(Dispatchers.IO){
futureChannel.await().write(ByteBuffer.wrap(data))
}
@OptIn(ExperimentalCoroutinesApi::class)
override fun close() {
listenerJob.cancel()
if (futureChannel.isCompleted) {
futureChannel.getCompleted().close()
} else {
futureChannel.cancel()
}
super.close()
}
public companion object : PortFactory {
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)
}
}
}

View File

@ -1,4 +0,0 @@
# Module controls-ktor-tcp

View File

@ -1,10 +0,0 @@
plugins {
id("space.kscience.gradle.jvm")
}
val ktorVersion: String by rootProject.extra
dependencies {
api(projects.controlsCore)
api("io.ktor:ktor-network:$ktorVersion")
}

View File

@ -1,32 +0,0 @@
# Module controls-magix-client
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.1.1-SNAPSHOT`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:controls-magix-client:0.1.1-SNAPSHOT'
}
```
**Gradle Kotlin DSL:**
```kotlin
repositories {
maven("https://repo.kotlin.link")
mavenCentral()
}
dependencies {
implementation("space.kscience:controls-magix-client:0.1.1-SNAPSHOT")
}
```

29
controls-magix/README.md Normal file
View File

@ -0,0 +1,29 @@
# 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
## Usage
## Artifact:
The Maven coordinates of this project are `space.kscience:controls-magix: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:controls-magix:0.2.0")
}
```

View File

@ -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 <init> (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 <init> (ILspace/kscience/controls/client/DoocsAction;Ljava/lang/String;Lspace/kscience/controls/client/EqData;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (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 <init> (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 <init> (ILjava/lang/String;Lspace/kscience/dataforge/meta/Meta;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Long;Ljava/lang/String;)V
public synthetic fun <init> (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 <init> (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 <init> (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 <init> (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;
}

View File

@ -0,0 +1,39 @@
import space.kscience.gradle.Maturity
plugins {
id("space.kscience.gradle.mpp")
`maven-publish`
}
description = """
Magix service for binding controls devices (both as RPC client and server)
""".trimIndent()
kscience {
jvm()
js()
useSerialization {
json()
}
dependencies {
api(projects.magix.magixApi)
api(projects.controlsCore)
api("com.benasher44:uuid:0.8.0")
}
}
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()
}
}

View File

@ -6,18 +6,20 @@ import kotlinx.coroutines.newCoroutineContext
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import space.kscience.controls.api.* import space.kscience.controls.api.*
import space.kscience.controls.manager.DeviceManager
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.misc.DFExperimental
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.magix.api.MagixEndpoint 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 space.kscience.magix.api.subscribe
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
private fun stringUID() = uuid4().leastSignificantBits.toString(16) 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( public class DeviceClient(
override val context: Context, override val context: Context,
@ -95,14 +97,21 @@ public class DeviceClient(
it.action == actionName && it.requestId == id it.action == actionName && it.requestId == id
}.result }.result
} }
@DFExperimental
override val lifecycleState: DeviceLifecycleState = DeviceLifecycleState.OPEN
} }
/** /**
* 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 { public fun MagixEndpoint.remoteDevice(context: Context, endpointName: String, deviceName: Name): DeviceClient {
val subscription = subscribe(controlsMagixFormat, originFilter = listOf(magixTarget)).map { it.second } val subscription = subscribe(DeviceManager.magixFormat, originFilter = listOf(endpointName)).map { it.second }
return DeviceClient(context, deviceName, subscription) { return DeviceClient(context, deviceName, subscription) {
broadcast(controlsMagixFormat, it, magixTarget, id = stringUID()) send(DeviceManager.magixFormat, it, endpointName, id = stringUID())
} }
} }

View File

@ -14,31 +14,37 @@ import space.kscience.dataforge.context.logger
import space.kscience.magix.api.* import space.kscience.magix.api.*
public val controlsMagixFormat: MagixFormat<DeviceMessage> = MagixFormat( internal val controlsMagixFormat: MagixFormat<DeviceMessage> = MagixFormat(
DeviceMessage.serializer(), DeviceMessage.serializer(),
setOf("controls-kt", "dataforge") setOf("controls-kt")
) )
/**
* A magix message format to work with Controls-kt data
*/
public val DeviceManager.Companion.magixFormat: MagixFormat<DeviceMessage> get() = controlsMagixFormat
internal fun generateId(request: MagixMessage): String = if (request.id != null) { internal fun generateId(request: MagixMessage): String = if (request.id != null) {
"${request.id}.response" "${request.id}.response"
} else { } else {
"df[${request.payload.hashCode()}" "controls[${request.payload.hashCode().toString(16)}"
} }
/** /**
* Communicate with server in [Magix format](https://github.com/waltz-controls/rfc/tree/master/1) * 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, endpoint: MagixEndpoint,
endpointID: String = controlsMagixFormat.defaultFormat, endpointID: String = controlsMagixFormat.defaultFormat,
): Job = context.launch { ): Job = context.launch {
endpoint.subscribe(controlsMagixFormat, targetFilter = listOf(endpointID)).onEach { (request, payload) -> endpoint.subscribe(controlsMagixFormat, targetFilter = listOf(endpointID)).onEach { (request, payload) ->
val responsePayload = respondHubMessage(payload) val responsePayload = respondHubMessage(payload)
if (responsePayload != null) { if (responsePayload != null) {
endpoint.broadcast( endpoint.send(
format = controlsMagixFormat, format = controlsMagixFormat,
origin = endpointID,
payload = responsePayload, payload = responsePayload,
source = endpointID,
target = request.sourceEndpoint,
id = generateId(request), id = generateId(request),
parentId = request.id parentId = request.id
) )
@ -48,10 +54,10 @@ public fun DeviceManager.connectToMagix(
}.launchIn(this) }.launchIn(this)
hubMessageFlow(this).onEach { payload -> hubMessageFlow(this).onEach { payload ->
endpoint.broadcast( endpoint.send(
format = controlsMagixFormat, format = controlsMagixFormat,
origin = endpointID,
payload = payload, payload = payload,
source = endpointID,
id = "df[${payload.hashCode()}]" id = "df[${payload.hashCode()}]"
) )
}.catch { error -> }.catch { error ->

View File

@ -75,12 +75,12 @@ public fun DeviceManager.launchTangoMagix(
): Job { ): Job {
suspend fun respond(request: MagixMessage, payload: TangoPayload, payloadBuilder: (TangoPayload) -> TangoPayload) { suspend fun respond(request: MagixMessage, payload: TangoPayload, payloadBuilder: (TangoPayload) -> TangoPayload) {
endpoint.broadcast( endpoint.send(
tangoMagixFormat, tangoMagixFormat,
payload = payloadBuilder(payload),
source = endpointID,
id = generateId(request), id = generateId(request),
parentId = request.id, parentId = request.id
origin = endpointID,
payload = payloadBuilder(payload)
) )
} }
@ -125,12 +125,12 @@ public fun DeviceManager.launchTangoMagix(
} }
} catch (ex: Exception) { } catch (ex: Exception) {
logger.error(ex) { "Error while responding to message" } logger.error(ex) { "Error while responding to message" }
endpoint.broadcast( endpoint.send(
tangoMagixFormat, tangoMagixFormat,
payload = payload.copy(quality = TangoQuality.WARNING),
source = endpointID,
id = generateId(request), id = generateId(request),
parentId = request.id, parentId = request.id
origin = endpointID,
payload = payload.copy(quality = TangoQuality.WARNING)
) )
} }
}.launchIn(this) }.launchIn(this)

View File

@ -2,3 +2,30 @@
A plugin for Controls-kt device server on top of modbus-rtu/modbus-tcp protocols 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`.
**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")
}
```

View File

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

View File

@ -1,5 +1,8 @@
import space.kscience.gradle.Maturity
plugins { plugins {
id("space.kscience.gradle.jvm") id("space.kscience.gradle.jvm")
`maven-publish`
} }
description = """ description = """
@ -11,3 +14,26 @@ dependencies {
api(projects.controlsCore) api(projects.controlsCore)
api("com.ghgande:j2mod:3.1.1") 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()
}
}

View File

@ -0,0 +1,219 @@
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 kotlinx.coroutines.launch
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 DeviceProcessImageBuilder<D : Device> internal constructor(
private val device: D,
public val image: ProcessImageImplementation,
) {
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<D, Boolean>,
): 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<D, Boolean>,
): 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<D, Short>,
): 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<D, Short>,
): ObservableRegister = bind(key) { register ->
register.addObserver { _, _ ->
device[propertySpec] = register.toShort()
}
device.useProperty(propertySpec) { value ->
register.setValue(value)
}
}
public fun <T> bind(key: ModbusRegistryKey.InputRange<T>, propertySpec: DevicePropertySpec<D, T>) {
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 <T> bind(key: ModbusRegistryKey.HoldingRange<T>, propertySpec: WritableDevicePropertySpec<D, T>) {
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 <T> bindAction(
key: ModbusRegistryKey.HoldingRange<T>,
action: suspend D.(T) -> Unit,
): List<ObservableRegister> {
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
}
}
/**
* Bind the device to Modbus slave (server) image.
*/
public fun <D : Device> D.bindProcessImage(
openOnBind: Boolean = true,
binding: DeviceProcessImageBuilder<D>.() -> Unit,
): ProcessImage {
val image = SimpleProcessImage()
DeviceProcessImageBuilder(this, image).apply(binding)
if (openOnBind) {
launch {
open()
}
}
return image
}

View File

@ -3,8 +3,12 @@ package space.kscience.controls.modbus
import com.ghgande.j2mod.modbus.facade.AbstractModbusMaster import com.ghgande.j2mod.modbus.facade.AbstractModbusMaster
import com.ghgande.j2mod.modbus.procimg.InputRegister import com.ghgande.j2mod.modbus.procimg.InputRegister
import com.ghgande.j2mod.modbus.procimg.Register 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 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 space.kscience.controls.api.Device
import java.nio.ByteBuffer import java.nio.ByteBuffer
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
@ -22,37 +26,91 @@ public interface ModbusDevice : Device {
public val clientId: Int public val clientId: Int
/** /**
* The OPC-UA client initialized on first use * The modubus master connector
*/ */
public val master: AbstractModbusMaster 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 <T> ModbusRegistryKey.InputRange<T>.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 <T> ModbusRegistryKey.HoldingRange<T>.getValue(thisRef: Any?, property: KProperty<*>): T {
val packet = readInputRegistersToPacket(address, count)
return format.readObject(packet)
}
public operator fun <T> ModbusRegistryKey.HoldingRange<T>.setValue(
thisRef: Any?,
property: KProperty<*>,
value: T,
) {
val buffer = buildPacket {
format.writeObject(this, value)
}.readByteBuffer()
writeHoldingRegisters(address, buffer)
}
} }
/** /**
* Read multiple sequential modbus coils (bit-values) * Read multiple sequential modbus coils (bit-values)
*/ */
public fun ModbusDevice.readCoils(ref: Int, count: Int): BitVector = public fun ModbusDevice.readCoils(address: Int, count: Int): BitVector =
master.readCoils(clientId, ref, count) master.readCoils(clientId, address, count)
public fun ModbusDevice.readCoil(ref: Int): Boolean = public fun ModbusDevice.readCoil(address: Int): Boolean =
master.readCoils(clientId, ref, 1).getBit(0) 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) val bitVector = BitVector(values.size)
values.forEachIndexed { index, value -> values.forEachIndexed { index, value ->
bitVector.setBit(index, value) bitVector.setBit(index, value)
} }
master.writeMultipleCoils(clientId, ref, bitVector) master.writeMultipleCoils(clientId, address, bitVector)
} }
public fun ModbusDevice.writeCoil(ref: Int, value: Boolean) { public fun ModbusDevice.writeCoil(address: Int, value: Boolean) {
master.writeCoil(clientId, ref, value) master.writeCoil(clientId, address, value)
} }
public fun ModbusDevice.readInputDiscretes(ref: Int, count: Int): BitVector = public fun ModbusDevice.writeCoil(key: ModbusRegistryKey.Coil, value: Boolean) {
master.readInputDiscretes(clientId, ref, count) master.writeCoil(clientId, key.address, value)
}
public fun ModbusDevice.readInputRegisters(ref: Int, count: Int): List<InputRegister> = public fun ModbusDevice.readInputDiscretes(address: Int, count: Int): BitVector =
master.readInputRegisters(clientId, ref, count).toList() master.readInputDiscretes(clientId, address, count)
public fun ModbusDevice.readInputDiscrete(address: Int): Boolean =
master.readInputDiscretes(clientId, address, 1).getBit(0)
public fun ModbusDevice.readInputRegisters(address: Int, count: Int): List<InputRegister> =
master.readInputRegisters(clientId, address, count).toList()
private fun Array<out InputRegister>.toBuffer(): ByteBuffer { private fun Array<out InputRegister>.toBuffer(): ByteBuffer {
val buffer: ByteBuffer = ByteBuffer.allocate(size * 2) val buffer: ByteBuffer = ByteBuffer.allocate(size * 2)
@ -64,83 +122,88 @@ private fun Array<out InputRegister>.toBuffer(): ByteBuffer {
return buffer return buffer
} }
public fun ModbusDevice.readInputRegistersToBuffer(ref: Int, count: Int): ByteBuffer = private fun Array<out InputRegister>.toPacket(): ByteReadPacket = buildPacket {
master.readInputRegisters(clientId, ref, count).toBuffer() forEach { value ->
writeShort(value.toShort())
public fun ModbusDevice.readDoubleInput(ref: Int): Double =
readInputRegistersToBuffer(ref, Double.SIZE_BYTES).getDouble()
public fun ModbusDevice.readShortInput(ref: Int): Short =
readInputRegisters(ref, 1).first().toShort()
public fun ModbusDevice.readHoldingRegisters(ref: Int, count: Int): List<Register> =
master.readMultipleRegisters(clientId, ref, count).toList()
public fun ModbusDevice.readHoldingRegistersToBuffer(ref: Int, count: Int): ByteBuffer =
master.readMultipleRegisters(clientId, ref, count).toBuffer()
public fun ModbusDevice.readDoubleRegister(ref: Int): Double =
readHoldingRegistersToBuffer(ref, Double.SIZE_BYTES).getDouble()
public fun ModbusDevice.readShortRegister(ref: Int): Short =
readHoldingRegisters(ref, 1).first().toShort()
public fun ModbusDevice.writeHoldingRegisters(ref: Int, values: ShortArray): Int =
master.writeMultipleRegisters(
clientId,
ref,
Array<Register>(values.size) { SimpleRegister().apply { setValue(values[it]) } }
)
public fun ModbusDevice.writeHoldingRegister(ref: Int, value: Short): Int =
master.writeSingleRegister(
clientId,
ref,
SimpleRegister().apply { setValue(value) }
)
public fun ModbusDevice.writeHoldingRegisters(ref: Int, buffer: ByteBuffer): Int {
val array = ShortArray(buffer.limit().floorDiv(2)) { buffer.getShort(it * 2) }
return writeHoldingRegisters(ref, array)
}
public fun ModbusDevice.writeShortRegister(ref: Int, value: Short) {
master.writeSingleRegister(ref, SimpleRegister().apply { setValue(value) })
}
public fun ModbusDevice.modBusRegister(
ref: Int,
): ReadWriteProperty<ModbusDevice, Short> = object : ReadWriteProperty<ModbusDevice, Short> {
override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Short = readShortRegister(ref)
override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Short) {
writeHoldingRegister(ref, value)
} }
} }
public fun ModbusDevice.modBusDoubleRegister( public fun ModbusDevice.readInputRegistersToBuffer(address: Int, count: Int): ByteBuffer =
ref: Int, master.readInputRegisters(clientId, address, count).toBuffer()
public fun ModbusDevice.readInputRegistersToPacket(address: Int, count: Int): ByteReadPacket =
master.readInputRegisters(clientId, address, count).toPacket()
public fun ModbusDevice.readDoubleInput(address: Int): Double =
readInputRegistersToBuffer(address, Double.SIZE_BYTES).getDouble()
public fun ModbusDevice.readInputRegister(address: Int): Short =
readInputRegisters(address, 1).first().toShort()
public fun ModbusDevice.readHoldingRegisters(address: Int, count: Int): List<Register> =
master.readMultipleRegisters(clientId, address, count).toList()
/**
* Read a number of registers to a [ByteBuffer]
* @param address of a register
* @param count number of 2-bytes registers to read. Buffer size is 2*[count]
*/
public fun ModbusDevice.readHoldingRegistersToBuffer(address: Int, count: Int): ByteBuffer =
master.readMultipleRegisters(clientId, address, count).toBuffer()
public fun ModbusDevice.readHoldingRegistersToPacket(address: Int, count: Int): ByteReadPacket =
master.readMultipleRegisters(clientId, address, count).toPacket()
public fun ModbusDevice.readDoubleRegister(address: Int): Double =
readHoldingRegistersToBuffer(address, Double.SIZE_BYTES).getDouble()
public fun ModbusDevice.readHoldingRegister(address: Int): Short =
readHoldingRegisters(address, 1).first().toShort()
public fun ModbusDevice.writeHoldingRegisters(address: Int, values: ShortArray): Int =
master.writeMultipleRegisters(
clientId,
address,
Array<Register>(values.size) { SimpleInputRegister(values[it].toInt()) }
)
public fun ModbusDevice.writeHoldingRegister(address: Int, value: Short): Int =
master.writeSingleRegister(
clientId,
address,
SimpleInputRegister(value.toInt())
)
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(address, array)
}
public fun ModbusDevice.writeShortRegister(address: Int, value: Short) {
master.writeSingleRegister(address, SimpleInputRegister(value.toInt()))
}
public fun ModbusDevice.modbusRegister(
address: Int,
): ReadWriteProperty<ModbusDevice, Short> = object : ReadWriteProperty<ModbusDevice, Short> {
override fun getValue(thisRef: ModbusDevice, property: KProperty<*>): Short = readHoldingRegister(address)
override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Short) {
writeHoldingRegister(address, value)
}
}
public fun ModbusDevice.modbusDoubleRegister(
address: Int,
): ReadWriteProperty<ModbusDevice, Double> = object : ReadWriteProperty<ModbusDevice, Double> { ): ReadWriteProperty<ModbusDevice, Double> = object : ReadWriteProperty<ModbusDevice, Double> {
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) { override fun setValue(thisRef: ModbusDevice, property: KProperty<*>, value: Double) {
val buffer = ByteBuffer.allocate(Double.SIZE_BYTES).apply { putDouble(value) } val buffer = ByteBuffer.allocate(Double.SIZE_BYTES).apply { putDouble(value) }
writeHoldingRegisters(ref, buffer) writeHoldingRegisters(address, buffer)
} }
} }
//
//public inline fun <reified T> ModbusDevice.opcDouble(
//): ReadWriteProperty<Any?, Double> = ma
//
//public inline fun <reified T> ModbusDeviceBySpec<*>.opcInt(
// nodeId: NodeId,
// magAge: Double = 1.0,
//): ReadWriteProperty<Any?, Int> = opc(nodeId, MetaConverter.int, magAge)
//
//public inline fun <reified T> ModbusDeviceBySpec<*>.opcString(
// nodeId: NodeId,
// magAge: Double = 1.0,
//): ReadWriteProperty<Any?, String> = opc(nodeId, MetaConverter.string, magAge)

View File

@ -17,8 +17,21 @@ public open class ModbusDeviceBySpec<D: Device>(
spec: DeviceSpec<D>, spec: DeviceSpec<D>,
override val clientId: Int, override val clientId: Int,
override val master: AbstractModbusMaster, override val master: AbstractModbusMaster,
private val disposeMasterOnClose: Boolean = true,
meta: Meta = Meta.EMPTY, meta: Meta = Meta.EMPTY,
) : ModbusDevice, DeviceBySpec<D>(spec, context, meta) ) : ModbusDevice, DeviceBySpec<D>(spec, context, meta){
override suspend fun open() {
master.connect()
super<DeviceBySpec>.open()
}
override fun close() {
if(disposeMasterOnClose){
master.disconnect()
}
super<ModbusDevice>.close()
}
}
public class ModbusHub( public class ModbusHub(

View File

@ -1,44 +1,163 @@
package space.kscience.controls.modbus package space.kscience.controls.modbus
import space.kscience.dataforge.io.IOFormat
public sealed class ModbusRegistryKey { public sealed class ModbusRegistryKey {
public abstract val address: Int
public open val count: Int = 1
/** /**
* Read-only boolean value * 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" }
}
}
/** /**
* Read-write boolean value * 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" } /**
} * Read-only binary value
*/
public open class InputRegister(override val address: Int) : ModbusRegistryKey() {
override fun toString(): String = "InputRegister(address=$address)"
} }
public class InputRegister(public val address: Int) : ModbusRegistryKey() { public class InputRange<T>(
init { address: Int,
require(address in 20001..29999) { "InputRegister address must be in 20001..29999 range" } override val count: Int,
} public val format: IOFormat<T>,
) : InputRegister(address) {
public val endAddress: Int get() = address + count
override fun toString(): String = "InputRange(count=$count, format=$format)"
} }
public class HoldingRegister(public val address: Int) : ModbusRegistryKey() { public open class HoldingRegister(override val address: Int) : ModbusRegistryKey() {
init { override fun toString(): String = "HoldingRegister(address=$address)"
require(address in 30001..39999) { "HoldingRegister address must be in 30001..39999 range" }
} }
public class HoldingRange<T>(
address: Int,
override val count: Int,
public val format: IOFormat<T>,
) : HoldingRegister(address) {
public val endAddress: Int get() = address + count
override fun toString(): String = "HoldingRange(count=$count, format=$format)"
} }
} }
public abstract class ModbusRegistryMap { public abstract class ModbusRegistryMap {
protected fun coil(address: Int): ModbusRegistryKey.Coil = ModbusRegistryKey.Coil(address)
protected fun discrete(address: Int): ModbusRegistryKey.DiscreteInput = ModbusRegistryKey.DiscreteInput(address) private val _entries: MutableMap<ModbusRegistryKey, String> = mutableMapOf<ModbusRegistryKey, String>()
protected fun input(address: Int): ModbusRegistryKey.InputRegister = ModbusRegistryKey.InputRegister(address) public val entries: Map<ModbusRegistryKey, String> get() = _entries
protected fun register(address: Int): ModbusRegistryKey.HoldingRegister = ModbusRegistryKey.HoldingRegister(address) protected fun <T : ModbusRegistryKey> register(key: T, description: String): T {
_entries[key] = description
return key
}
protected fun coil(address: Int, description: String = ""): ModbusRegistryKey.Coil =
register(ModbusRegistryKey.Coil(address), description)
protected fun discrete(address: Int, description: String = ""): ModbusRegistryKey.DiscreteInput =
register(ModbusRegistryKey.DiscreteInput(address), description)
protected fun input(address: Int, description: String = ""): ModbusRegistryKey.InputRegister =
register(ModbusRegistryKey.InputRegister(address), description)
protected fun <T> input(
address: Int,
count: Int,
reader: IOFormat<T>,
description: String = "",
): ModbusRegistryKey.InputRange<T> =
register(ModbusRegistryKey.InputRange(address, count, reader), description)
protected fun register(address: Int, description: String = ""): ModbusRegistryKey.HoldingRegister =
register(ModbusRegistryKey.HoldingRegister(address), description)
protected fun <T> register(
address: Int,
count: Int,
format: IOFormat<T>,
description: String = "",
): ModbusRegistryKey.HoldingRange<T> =
register(ModbusRegistryKey.HoldingRange(address, count, format), description)
public companion object {
public fun validate(map: ModbusRegistryMap) {
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
.sortedWith(
Comparator.comparingInt<Map.Entry<ModbusRegistryKey, String>?> { 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")
}
}
}
} }

View File

@ -1,4 +1,29 @@
# Module controls-opcua # 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`.
**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")
}
```

View File

@ -0,0 +1,82 @@
public final class space/kscience/controls/opcua/client/MetaBsdParser : org/eclipse/milo/opcua/binaryschema/parser/BsdParser {
public fun <init> ()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 <init> ()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 <init> ()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 <init> (Lspace/kscience/controls/spec/DeviceSpec;Lspace/kscience/controls/opcua/client/MiloConfiguration;Lspace/kscience/dataforge/context/Context;)V
public synthetic fun <init> (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 <init> (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
}

View File

@ -1,10 +1,17 @@
import space.kscience.gradle.Maturity
plugins { plugins {
id("space.kscience.gradle.jvm") 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 ktorVersion: String by rootProject.extra
val miloVersion: String = "0.6.9" val miloVersion: String = "0.6.10"
dependencies { dependencies {
api(projects.controlsCore) api(projects.controlsCore)
@ -16,3 +23,19 @@ dependencies {
testImplementation(spclibs.kotlinx.coroutines.test) 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()
}
}

View File

@ -1,5 +1,7 @@
package space.kscience.controls.opcua.client 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.AbstractCodec
import org.eclipse.milo.opcua.binaryschema.parser.BsdParser import org.eclipse.milo.opcua.binaryschema.parser.BsdParser
import org.eclipse.milo.opcua.stack.core.UaSerializationException 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 Boolean -> Meta(value.asValue())
is String -> Meta(value.asValue()) is String -> Meta(value.asValue())
is Char -> Meta(value.toString().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 UUID -> Meta(value.toString().asValue())
is QualifiedName -> Meta { is QualifiedName -> Meta {
"namespaceIndex" put value.namespaceIndex "namespaceIndex" put value.namespaceIndex
@ -79,9 +81,9 @@ internal fun opcToMeta(value: Any?): Meta = when (value) {
is DataValue -> Meta { is DataValue -> Meta {
"value" put opcToMeta(value.value) // need SerializationContext to do that properly "value" put opcToMeta(value.value) // need SerializationContext to do that properly
value.statusCode?.value?.let { "status" put Meta(it.asValue()) } 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.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()) } value.serverPicoseconds?.let { "serverPicoseconds" put Meta(it.asValue()) }
} }
is ByteString -> Meta(value.bytesOrEmpty().asValue()) is ByteString -> Meta(value.bytesOrEmpty().asValue())
@ -145,7 +147,7 @@ internal class MetaStructureCodec(
"Float" -> member.value?.numberOrNull?.toFloat() "Float" -> member.value?.numberOrNull?.toFloat()
"Double" -> member.value?.numberOrNull?.toDouble() "Double" -> member.value?.numberOrNull?.toDouble()
"String" -> member.string "String" -> member.string
"DateTime" -> DateTime(member.instant()) "DateTime" -> DateTime(member.instant().toJavaInstant())
"Guid" -> member.string?.let { UUID.fromString(it) } "Guid" -> member.string?.let { UUID.fromString(it) }
"ByteString" -> member.value?.list?.let { list -> "ByteString" -> member.value?.list?.let { list ->
ByteString(list.map { it.number.toByte() }.toByteArray()) ByteString(list.map { it.number.toByte() }.toByteArray())

View File

@ -1,7 +1,7 @@
package space.kscience.controls.opcua.client package space.kscience.controls.opcua.client
import org.eclipse.milo.opcua.sdk.client.OpcUaClient 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.sdk.client.api.identity.UsernameProvider
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy
import space.kscience.controls.api.Device import space.kscience.controls.api.Device
@ -12,12 +12,12 @@ import space.kscience.dataforge.context.Global
import space.kscience.dataforge.meta.* import space.kscience.dataforge.meta.*
public sealed class MiloIdentity: Scheme() public sealed class MiloIdentity : Scheme()
public class MiloUsername : MiloIdentity() { public class MiloUsername : MiloIdentity() {
public var username: String by string{ error("Username not defined") } public var username: String by string { error("Username not defined") }
public var password: String by string{ error("Password not defined") } public var password: String by string { error("Password not defined") }
public companion object : SchemeSpec<MiloUsername>(::MiloUsername) public companion object : SchemeSpec<MiloUsername>(::MiloUsername)
} }
@ -35,6 +35,12 @@ public class MiloConfiguration : Scheme() {
public var securityPolicy: SecurityPolicy by enum(SecurityPolicy.None) 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>(::MiloConfiguration) public companion object : SchemeSpec<MiloConfiguration>(::MiloConfiguration)
} }
@ -51,9 +57,7 @@ public open class OpcUaDeviceBySpec<D : Device>(
context.createOpcUaClient( context.createOpcUaClient(
config.endpointUrl, config.endpointUrl,
securityPolicy = config.securityPolicy, securityPolicy = config.securityPolicy,
identityProvider = config.username?.let { opcClientConfig = { config.configureClient(this) }
UsernameProvider(it.username,it.password)
} ?: AnonymousProvider()
).apply { ).apply {
connect().get() connect().get()
} }

View File

@ -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.OpcUaClient
import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfigBuilder 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.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.client.security.DefaultClientCertificateValidator
import org.eclipse.milo.opcua.stack.core.security.DefaultTrustListManager import org.eclipse.milo.opcua.stack.core.security.DefaultTrustListManager
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy 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.nio.file.Paths
import java.util.* import java.util.*
internal fun <T:Any> T?.toOptional(): Optional<T> = if(this == null) Optional.empty() else Optional.of(this) internal fun <T : Any> T?.toOptional(): Optional<T> = Optional.ofNullable(this)
internal fun Context.createOpcUaClient( internal fun Context.createOpcUaClient(
endpointUrl: String, //"opc.tcp://localhost:12686/milo" endpointUrl: String, //"opc.tcp://localhost:12686/milo"
securityPolicy: SecurityPolicy = SecurityPolicy.Basic256Sha256, 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 { ): OpcUaClient {
val securityTempDir: Path = Paths.get(System.getProperty("java.io.tmpdir"), "client", "security") val securityTempDir: Path = Paths.get(System.getProperty("java.io.tmpdir"), "client", "security")
@ -47,14 +46,15 @@ internal fun Context.createOpcUaClient(
} }
) { configBuilder: OpcUaClientConfigBuilder -> ) { configBuilder: OpcUaClientConfigBuilder ->
configBuilder configBuilder
.setApplicationName(LocalizedText.english("Controls.kt")) .setApplicationName(LocalizedText.english("Controls-kt"))
.setApplicationUri("urn:ru.mipt:npm:controls:opcua") .setApplicationUri("urn:space.kscience:controls:opcua")
// .setKeyPair(loader.getClientKeyPair()) // .setKeyPair(loader.getClientKeyPair())
// .setCertificate(loader.getClientCertificate()) // .setCertificate(loader.getClientCertificate())
// .setCertificateChain(loader.getClientCertificateChain()) // .setCertificateChain(loader.getClientCertificateChain())
.setCertificateValidator(certificateValidator) .setCertificateValidator(certificateValidator)
.setIdentityProvider(identityProvider) .setIdentityProvider(AnonymousProvider())
.setRequestTimeout(uint(5000)) .setRequestTimeout(uint(5000))
.apply(opcClientConfig)
.build() .build()
} }
// .apply { // .apply {

View File

@ -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.api.MonitoredItem
import org.eclipse.milo.opcua.sdk.server.nodes.UaFolderNode 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.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.nodes.UaVariableNode
import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel import org.eclipse.milo.opcua.sdk.server.util.SubscriptionModel
import org.eclipse.milo.opcua.stack.core.AttributeId 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.MetaSerializer
import space.kscience.dataforge.meta.ValueType import space.kscience.dataforge.meta.ValueType
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus import space.kscience.dataforge.names.plus
@ -50,25 +50,7 @@ public class DeviceNameSpace(
lifecycleManager.addLifecycle(subscription) lifecycleManager.addLifecycle(subscription)
lifecycleManager.addStartupTask { lifecycleManager.addStartupTask {
deviceManager.devices.forEach { (deviceName, device) -> nodeContext.registerHub(deviceManager, Name.EMPTY)
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)
}
} }
lifecycleManager.addLifecycle(object : Lifecycle { lifecycleManager.addLifecycle(object : Lifecycle {
@ -88,7 +70,7 @@ public class DeviceNameSpace(
val node: UaVariableNode = UaVariableNode.UaVariableNodeBuilder(nodeContext).apply { 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") nodeId = newNodeId("${deviceName.tokens.joinToString("/")}/$propertyName")
when { when {
descriptor.readable && descriptor.writable -> { descriptor.readable && descriptor.writable -> {
@ -161,15 +143,15 @@ public class DeviceNameSpace(
} }
//recursively add sub-devices //recursively add sub-devices
if (device is DeviceHub) { 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) -> hub.devices.forEach { (deviceName, device) ->
val tokenAsString = deviceName.toString() val tokenAsString = deviceName.toString()
val deviceFolder = UaFolderNode( val deviceFolder = UaFolderNode(
this.nodeContext, this,
newNodeId(tokenAsString), newNodeId(tokenAsString),
newQualifiedName(tokenAsString), newQualifiedName(tokenAsString),
LocalizedText.english(tokenAsString) LocalizedText.english(tokenAsString)

View File

@ -8,6 +8,7 @@ import space.kscience.controls.spec.DeviceSpec
import space.kscience.controls.spec.doubleProperty import space.kscience.controls.spec.doubleProperty
import space.kscience.controls.spec.read import space.kscience.controls.spec.read
import space.kscience.dataforge.meta.transformations.MetaConverter import space.kscience.dataforge.meta.transformations.MetaConverter
import kotlin.test.Ignore
class OpcUaClientTest { class OpcUaClientTest {
class DemoOpcUaDevice(config: MiloConfiguration) : OpcUaDeviceBySpec<DemoOpcUaDevice>(DemoOpcUaDevice, config) { class DemoOpcUaDevice(config: MiloConfiguration) : OpcUaDeviceBySpec<DemoOpcUaDevice>(DemoOpcUaDevice, config) {
@ -37,6 +38,7 @@ class OpcUaClientTest {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
@Test @Test
@Ignore
fun testReadDouble() = runTest { fun testReadDouble() = runTest {
DemoOpcUaDevice.build().use{ DemoOpcUaDevice.build().use{
println(it.read(DemoOpcUaDevice.randomDouble)) println(it.read(DemoOpcUaDevice.randomDouble))

23
controls-pi/README.md Normal file
View File

@ -0,0 +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`.
**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")
}
```

View File

@ -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 <init> ()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 <init> (Lspace/kscience/dataforge/context/Context;Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function0;)V
public synthetic fun <init> (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;
}

View File

@ -0,0 +1,16 @@
plugins {
id("space.kscience.gradle.jvm")
`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
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")
}

View File

@ -0,0 +1,22 @@
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
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<PiPlugin> {
override val tag: PluginTag = PluginTag("controls.ports.pi", group = PluginTag.DATAFORGE_GROUP)
override fun build(context: Context, meta: Meta): PiPlugin = PiPlugin()
}
}

View File

@ -0,0 +1,75 @@
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
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 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
public class PiSerialPort(
context: Context,
coroutineContext: CoroutineContext = context.coroutineContext,
public val serialBuilder: () -> Serial,
) : AbstractPort(context, coroutineContext) {
private val serial: Serial by lazy { serialBuilder() }
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)
}
}
}
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) {
val device: String = meta["device"].string ?: error("Device name not defined")
val baudRate: Baud = meta["baudRate"].enum<Baud>() ?: Baud._9600
Pi4J.newAutoContext().serial(device) {
baud8N1(baudRate)
}
}
}
}

View File

@ -0,0 +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`.
**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")
}
```

View File

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

View File

@ -0,0 +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
}

View File

@ -6,21 +6,22 @@ import space.kscience.dataforge.context.PluginFactory
import space.kscience.dataforge.context.PluginTag import space.kscience.dataforge.context.PluginTag
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.names.Name 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 val tag: PluginTag get() = Companion.tag
override fun content(target: String): Map<Name, Any> = when(target){ override fun content(target: String): Map<Name, Any> = when (target) {
PortFactory.TYPE -> mapOf(Name.EMPTY to KtorTcpPort) PortFactory.TYPE -> mapOf("tcp".asName() to KtorTcpPort, "udp".asName() to KtorUdpPort)
else -> emptyMap() else -> emptyMap()
} }
public companion object : PluginFactory<KtorTcpPortPlugin> { public companion object : PluginFactory<KtorPortsPlugin> {
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()
} }

View File

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

View File

@ -1,32 +1,23 @@
# Module controls-serial # Module controls-serial
Implementation of direct serial port communication with JSerialComm
## Usage ## Usage
## Artifact: ## 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`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:controls-serial:0.1.1-SNAPSHOT'
}
```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
```kotlin ```kotlin
repositories { repositories {
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")
//uncomment to access development builds
//maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
implementation("space.kscience:controls-serial:0.1.1-SNAPSHOT") implementation("space.kscience:controls-serial:0.2.0")
} }
``` ```

View File

@ -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 <init> (Lspace/kscience/dataforge/context/Context;Lcom/fazecast/jSerialComm/SerialPort;Lkotlin/coroutines/CoroutineContext;)V
public synthetic fun <init> (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 <init> ()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;
}

View File

@ -1,9 +1,17 @@
import space.kscience.gradle.Maturity
plugins { plugins {
id("space.kscience.gradle.jvm") id("space.kscience.gradle.jvm")
`maven-publish` `maven-publish`
} }
description = "Implementation of direct serial port communication with JSerialComm"
dependencies{ dependencies{
api(project(":controls-core")) api(project(":controls-core"))
implementation("org.scream3r:jssc:2.8.0") implementation("com.fazecast:jSerialComm:2.10.3")
}
readme{
maturity = Maturity.EXPERIMENTAL
} }

View File

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

View File

@ -1,92 +0,0 @@
package space.kscience.controls.serial
import jssc.SerialPort.*
import jssc.SerialPortEventListener
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)
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)
}
}
}

View File

@ -13,7 +13,7 @@ public class SerialPortPlugin : AbstractPlugin() {
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
override fun content(target: String): Map<Name, Any> = when(target){ override fun content(target: String): Map<Name, Any> = when(target){
PortFactory.TYPE -> mapOf(Name.EMPTY to SerialPort) PortFactory.TYPE -> mapOf(Name.EMPTY to JSerialCommPort)
else -> emptyMap() else -> emptyMap()
} }

View File

@ -1,32 +1,23 @@
# Module controls-server # 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 ## Usage
## Artifact: ## 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`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:controls-server:0.1.1-SNAPSHOT'
}
```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
```kotlin ```kotlin
repositories { repositories {
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")
//uncomment to access development builds
//maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
implementation("space.kscience:controls-server:0.1.1-SNAPSHOT") implementation("space.kscience:controls-server:0.2.0")
} }
``` ```

View File

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

View File

@ -1,10 +1,12 @@
import space.kscience.gradle.Maturity
plugins { plugins {
id("space.kscience.gradle.jvm") id("space.kscience.gradle.jvm")
`maven-publish` `maven-publish`
} }
description = """ description = """
A magix event loop server with web server for visualization. A combined Magix event loop server with web server for visualization.
""".trimIndent() """.trimIndent()
val dataforgeVersion: String by rootProject.extra val dataforgeVersion: String by rootProject.extra
@ -12,7 +14,7 @@ val ktorVersion: String by rootProject.extra
dependencies { dependencies {
implementation(projects.controlsCore) implementation(projects.controlsCore)
implementation(projects.controlsKtorTcp) implementation(projects.controlsPortsKtor)
implementation(projects.magix.magixServer) implementation(projects.magix.magixServer)
implementation("io.ktor:ktor-server-cio:$ktorVersion") implementation("io.ktor:ktor-server-cio:$ktorVersion")
implementation("io.ktor:ktor-server-websockets:$ktorVersion") implementation("io.ktor:ktor-server-websockets:$ktorVersion")
@ -21,3 +23,7 @@ dependencies {
implementation("io.ktor:ktor-server-html-builder:$ktorVersion") implementation("io.ktor:ktor-server-html-builder:$ktorVersion")
implementation("io.ktor:ktor-server-status-pages:$ktorVersion") implementation("io.ktor:ktor-server-status-pages:$ktorVersion")
} }
readme{
maturity = Maturity.PROTOTYPE
}

View File

@ -1,32 +1,23 @@
# Module controls-storage # Module controls-storage
An API for stand-alone Controls-kt device or a hub.
## Usage ## Usage
## Artifact: ## 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`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:controls-storage:0.1.1-SNAPSHOT'
}
```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
```kotlin ```kotlin
repositories { repositories {
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")
//uncomment to access development builds
//maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
implementation("space.kscience:controls-storage:0.1.1-SNAPSHOT") implementation("space.kscience:controls-storage:0.2.0")
} }
``` ```

View File

@ -5,6 +5,10 @@ plugins {
val dataforgeVersion: String by rootProject.extra val dataforgeVersion: String by rootProject.extra
description = """
An API for stand-alone Controls-kt device or a hub.
""".trimIndent()
kscience{ kscience{
jvm() jvm()
js() js()
@ -13,7 +17,7 @@ kscience{
} }
dependencies(jvmMain){ dependencies(jvmMain){
api(projects.magix.magixApi) api(projects.magix.magixApi)
api(projects.controlsMagixClient) api(projects.controlsMagix)
api(projects.magix.magixServer) api(projects.magix.magixServer)
} }
} }

View File

@ -1,32 +1,23 @@
# Module controls-xodus # Module controls-xodus
An implementation of controls-storage on top of JetBrains Xodus.
## Usage ## Usage
## Artifact: ## 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`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:controls-xodus:0.1.1-SNAPSHOT'
}
```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
```kotlin ```kotlin
repositories { repositories {
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")
//uncomment to access development builds
//maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
implementation("space.kscience:controls-xodus:0.1.1-SNAPSHOT") implementation("space.kscience:controls-xodus:0.2.0")
} }
``` ```

View File

@ -5,6 +5,10 @@ plugins {
val xodusVersion: String by rootProject.extra val xodusVersion: String by rootProject.extra
description = """
An implementation of controls-storage on top of JetBrains Xodus.
""".trimIndent()
dependencies { dependencies {
api(projects.controlsStorage) api(projects.controlsStorage)
implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion") implementation("org.jetbrains.xodus:xodus-entity-store:$xodusVersion")

View File

@ -0,0 +1,77 @@
public final class space/kscience/controls/demo/DemoController : tornadofx/Controller, space/kscience/dataforge/context/ContextAware {
public fun <init> ()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 <init> ()V
public fun start (Ljavafx/stage/Stage;)V
public fun stop ()V
}
public final class space/kscience/controls/demo/DemoControllerView : tornadofx/View {
public fun <init> ()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 <init> (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;
}

View File

@ -17,7 +17,7 @@ dependencies {
implementation(projects.controlsCore) implementation(projects.controlsCore)
//implementation(projects.controlsServer) //implementation(projects.controlsServer)
implementation(projects.magix.magixServer) implementation(projects.magix.magixServer)
implementation(projects.controlsMagixClient) implementation(projects.controlsMagix)
implementation(projects.magix.magixRsocket) implementation(projects.magix.magixRsocket)
implementation(projects.magix.magixZmq) implementation(projects.magix.magixZmq)
implementation(projects.controlsOpcua) implementation(projects.controlsOpcua)

View File

@ -8,7 +8,7 @@ import javafx.stage.Stage
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.eclipse.milo.opcua.sdk.server.OpcUaServer import org.eclipse.milo.opcua.sdk.server.OpcUaServer
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText 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.cosScale
import space.kscience.controls.demo.DemoDevice.Companion.sinScale import space.kscience.controls.demo.DemoDevice.Companion.sinScale
import space.kscience.controls.demo.DemoDevice.Companion.timeScale import space.kscience.controls.demo.DemoDevice.Companion.timeScale
@ -36,8 +36,9 @@ class DemoController : Controller(), ContextAware {
var visualizer: ApplicationEngine? = null var visualizer: ApplicationEngine? = null
var opcUaServer: OpcUaServer = OpcUaServer { var opcUaServer: OpcUaServer = OpcUaServer {
setApplicationName(LocalizedText.english("space.kscience.controls.opcua")) setApplicationName(LocalizedText.english("space.kscience.controls.opcua"))
endpoint { endpoint {
setBindPort(9999) setBindPort(4840)
//use default endpoint //use default endpoint
} }
} }
@ -58,7 +59,7 @@ class DemoController : Controller(), ContextAware {
) )
//Launch a device client and connect it to the server //Launch a device client and connect it to the server
val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost") val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost")
deviceManager.connectToMagix(deviceEndpoint) deviceManager.launchMagixService(deviceEndpoint)
//connect visualization to a magix endpoint //connect visualization to a magix endpoint
val visualEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost") val visualEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost")
visualizer = startDemoDeviceServer(visualEndpoint) visualizer = startDemoDeviceServer(visualEndpoint)

View File

@ -68,11 +68,10 @@ class DemoDevice(context: Context, meta: Meta) : DeviceBySpec<IDemoDevice>(Compa
} }
val resetScale by action(MetaConverter.meta, MetaConverter.meta) { val resetScale by unitAction {
write(timeScale, 5000.0) write(timeScale, 5000.0)
write(sinScale, 1.0) write(sinScale, 1.0)
write(cosScale, 1.0) write(cosScale, 1.0)
null
} }
override suspend fun IDemoDevice.onOpen() { override suspend fun IDemoDevice.onOpen() {

View File

@ -7,7 +7,8 @@ import kotlinx.coroutines.launch
import kotlinx.html.div import kotlinx.html.div
import kotlinx.html.link import kotlinx.html.link
import space.kscience.controls.api.PropertyChangedMessage 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.controls.spec.name
import space.kscience.dataforge.meta.double import space.kscience.dataforge.meta.double
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
@ -54,7 +55,7 @@ suspend fun Trace.updateXYFrom(flow: Flow<Iterable<Pair<Double, Double>>>) {
fun CoroutineScope.startDemoDeviceServer(magixEndpoint: MagixEndpoint): ApplicationEngine { fun CoroutineScope.startDemoDeviceServer(magixEndpoint: MagixEndpoint): ApplicationEngine {
//share subscription to a parse message only once //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) -> val sinFlow = subscription.mapNotNull { (_, payload) ->
(payload as? PropertyChangedMessage)?.takeIf { it.property == DemoDevice.sin.name } (payload as? PropertyChangedMessage)?.takeIf { it.property == DemoDevice.sin.name }

111
demo/car/api/car.api Normal file
View File

@ -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 <init> (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 <init> ()V
public fun <init> (DD)V
public synthetic fun <init> (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 <init> (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 <init> ()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 <init> ()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 <init> ()V
public fun getRoot ()Ljavafx/scene/Parent;
}

View File

@ -19,7 +19,7 @@ dependencies {
implementation(projects.magix.magixServer) implementation(projects.magix.magixServer)
implementation(projects.magix.magixRsocket) implementation(projects.magix.magixRsocket)
implementation(projects.magix.magixZmq) implementation(projects.magix.magixZmq)
implementation(projects.controlsMagixClient) implementation(projects.controlsMagix)
implementation(projects.controlsStorage.controlsXodus) implementation(projects.controlsStorage.controlsXodus)
implementation(projects.magix.magixStorage.magixStorageXodus) implementation(projects.magix.magixStorage.magixStorageXodus)
// implementation(projects.controlsMongo) // implementation(projects.controlsMongo)

View File

@ -2,7 +2,8 @@ package space.kscience.controls.demo.car
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import space.kscience.controls.api.PropertyChangedMessage 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.controls.spec.write
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.Factory import space.kscience.dataforge.context.Factory
@ -18,7 +19,7 @@ import kotlin.time.ExperimentalTime
class MagixVirtualCar(context: Context, meta: Meta) : VirtualCar(context, meta) { class MagixVirtualCar(context: Context, meta: Meta) : VirtualCar(context, meta) {
private fun MagixEndpoint.launchMagixVirtualCarUpdate() = launch { private fun MagixEndpoint.launchMagixVirtualCarUpdate() = launch {
subscribe(controlsMagixFormat).collect { (_, payload) -> subscribe(DeviceManager.magixFormat).collect { (_, payload) ->
(payload as? PropertyChangedMessage)?.let { message -> (payload as? PropertyChangedMessage)?.let { message ->
if (message.sourceDevice == Name.parse("virtual-car")) { if (message.sourceDevice == Name.parse("virtual-car")) {
when (message.property) { when (message.property) {

View File

@ -8,7 +8,7 @@ import javafx.scene.layout.Priority
import javafx.stage.Stage import javafx.stage.Stage
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch 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.demo.car.IVirtualCar.Companion.acceleration
import space.kscience.controls.manager.DeviceManager import space.kscience.controls.manager.DeviceManager
import space.kscience.controls.manager.install import space.kscience.controls.manager.install
@ -63,7 +63,7 @@ class VirtualCarController : Controller(), ContextAware {
//mongoStorageJob = deviceManager.storeMessages(DefaultAsynchronousMongoClientFactory) //mongoStorageJob = deviceManager.storeMessages(DefaultAsynchronousMongoClientFactory)
//Launch device client and connect it to the server //Launch device client and connect it to the server
val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost") val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost")
deviceManager.connectToMagix(deviceEndpoint) deviceManager.launchMagixService(deviceEndpoint)
} }
} }

5
demo/echo/api/echo.api Normal file
View File

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

View File

@ -22,7 +22,7 @@ private suspend fun MagixEndpoint.collectEcho(scope: CoroutineScope, n: Int) {
scope.launch { scope.launch {
subscribe( subscribe(
MagixMessageFilter( MagixMessageFilter(
origin = listOf("loop") source = listOf("loop")
) )
).collect { message -> ).collect { message ->
if (message.id?.endsWith(".response") == true) { if (message.id?.endsWith(".response") == true) {
@ -44,8 +44,8 @@ private suspend fun MagixEndpoint.collectEcho(scope: CoroutineScope, n: Int) {
MagixMessage( MagixMessage(
format = "test", format = "test",
payload = JsonObject(emptyMap()), payload = JsonObject(emptyMap()),
origin = "test", sourceEndpoint = "test",
target = "loop", targetEndpoint = "loop",
id = it.toString() id = it.toString()
) )
) )
@ -60,14 +60,14 @@ private suspend fun MagixEndpoint.collectEcho(scope: CoroutineScope, n: Int) {
@OptIn(ExperimentalTime::class) @OptIn(ExperimentalTime::class)
suspend fun main(): Unit = coroutineScope { suspend fun main(): Unit = coroutineScope {
launch(Dispatchers.Default) { launch(Dispatchers.Default) {
val server = startMagixServer(MagixFlowPlugin { _, flow -> val server = startMagixServer(MagixFlowPlugin { _, flow, send ->
val logger = LoggerFactory.getLogger("echo") val logger = LoggerFactory.getLogger("echo")
//echo each message //echo each message
flow.onEach { message -> flow.onEach { message ->
if (message.parentId == null) { 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()) logger.info(m.toString())
flow.emit(m) send(m)
} }
}.launchIn(this) }.launchIn(this)
}) })

View File

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

View File

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

View File

@ -14,12 +14,12 @@ val rsocketVersion: String by rootProject.extra
dependencies { dependencies {
implementation(projects.magix.magixServer) implementation(projects.magix.magixServer)
implementation(projects.controlsMagixClient) implementation(projects.controlsMagix)
implementation(projects.magix.magixRsocket) implementation(projects.magix.magixRsocket)
implementation(projects.magix.magixZmq) implementation(projects.magix.magixZmq)
implementation("io.ktor:ktor-client-cio:$ktorVersion") 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) implementation(spclibs.logback.classic)
} }

View File

@ -1,15 +1,13 @@
package space.kscience.controls.demo package space.kscience.controls.demo
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.isActive import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.launch import kotlinx.coroutines.sync.withLock
import kotlinx.datetime.Clock import kotlinx.datetime.Clock
import kotlinx.datetime.Instant import space.kscience.controls.client.launchMagixService
import space.kscience.controls.client.connectToMagix import space.kscience.controls.client.magixFormat
import space.kscience.controls.client.controlsMagixFormat
import space.kscience.controls.manager.DeviceManager import space.kscience.controls.manager.DeviceManager
import space.kscience.controls.manager.install import space.kscience.controls.manager.install
import space.kscience.controls.spec.* import space.kscience.controls.spec.*
@ -21,7 +19,8 @@ import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.int
import space.kscience.magix.api.MagixEndpoint import space.kscience.magix.api.MagixEndpoint
import space.kscience.magix.api.subscribe import space.kscience.magix.api.subscribe
import space.kscience.magix.rsocket.rSocketStreamWithTcp import space.kscience.magix.rsocket.rSocketWithTcp
import space.kscience.magix.rsocket.rSocketWithWebSockets
import space.kscience.magix.server.RSocketMagixFlowPlugin import space.kscience.magix.server.RSocketMagixFlowPlugin
import space.kscience.magix.server.startMagixServer import space.kscience.magix.server.startMagixServer
import space.kscience.plotly.Plotly 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.PlotlyUpdateMode
import space.kscience.plotly.server.serve import space.kscience.plotly.server.serve
import space.kscience.plotly.server.show import space.kscience.plotly.server.show
import java.util.concurrent.ConcurrentHashMap import space.kscince.magix.zmq.ZmqMagixFlowPlugin
import kotlin.random.Random import kotlin.random.Random
import kotlin.time.Duration
import kotlin.time.Duration.Companion.ZERO
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
@ -48,25 +49,27 @@ class MassDevice(context: Context, meta: Meta) : DeviceBySpec<MassDevice>(MassDe
val value by doubleProperty { randomValue } val value by doubleProperty { randomValue }
override suspend fun MassDevice.onOpen() { override suspend fun MassDevice.onOpen() {
doRecurring(100.milliseconds) { doRecurring((meta["delay"].int ?: 10).milliseconds) {
read(value) read(value)
} }
} }
} }
} }
fun main() { @OptIn(DelicateCoroutinesApi::class)
suspend fun main() {
val context = Context("Mass") val context = Context("Mass")
context.startMagixServer( context.startMagixServer(
RSocketMagixFlowPlugin(), RSocketMagixFlowPlugin(),
// ZmqMagixFlowPlugin() ZmqMagixFlowPlugin()
) )
val numDevices = 100 val numDevices = 100
context.launch(Dispatchers.IO) {
repeat(numDevices) { repeat(numDevices) {
context.launch(newFixedThreadPoolContext(2, "Device${it}")) {
delay(1)
val deviceContext = Context("Device${it}") { val deviceContext = Context("Device${it}") {
plugin(DeviceManager) plugin(DeviceManager)
} }
@ -76,8 +79,8 @@ fun main() {
deviceManager.install("device$it", MassDevice) deviceManager.install("device$it", MassDevice)
val endpointId = "device$it" val endpointId = "device$it"
val deviceEndpoint = MagixEndpoint.rSocketStreamWithTcp("localhost") val deviceEndpoint = MagixEndpoint.rSocketWithTcp("localhost")
deviceManager.connectToMagix(deviceEndpoint, endpointId) deviceManager.launchMagixService(deviceEndpoint, endpointId)
} }
} }
@ -88,23 +91,36 @@ fun main() {
plot(renderer = container) { plot(renderer = container) {
layout { layout {
title = "Latest event" title = "Latest event"
xaxis.title = "Device number"
yaxis.title = "Maximum latency in ms"
} }
bar { bar {
launch(Dispatchers.Default){ launch(Dispatchers.IO) {
val monitorEndpoint = MagixEndpoint.rSocketStreamWithTcp("localhost") val monitorEndpoint = MagixEndpoint.rSocketWithWebSockets("localhost")
val latest = ConcurrentHashMap<String, Instant>() val mutex = Mutex()
monitorEndpoint.subscribe(controlsMagixFormat).onEach { (magixMessage, payload) -> val latest = HashMap<String, Duration>()
latest[magixMessage.origin] = payload.time ?: Clock.System.now() val max = HashMap<String, Duration>()
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) }.launchIn(this)
while (isActive) { while (isActive) {
delay(200) delay(200)
val now = Clock.System.now() 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 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 }
}
} }
} }
} }

View File

@ -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 <init> (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;
}

View File

@ -17,5 +17,5 @@ val ktorVersion: String by rootProject.extra
val dataforgeVersion: String by extra val dataforgeVersion: String by extra
dependencies { dependencies {
implementation(projects.controlsKtorTcp) implementation(projects.controlsPortsKtor)
} }

115
demo/motors/api/motors.api Normal file
View File

@ -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 <init> ()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 <init> ()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 <init> (Lspace/kscience/dataforge/context/Context;Lspace/kscience/controls/ports/PortFactory;)V
public synthetic fun <init> (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 <init> (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 <init> ()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 <init> (Lspace/kscience/dataforge/context/Context;Ljava/util/List;Lkotlinx/coroutines/CoroutineScope;)V
public synthetic fun <init> (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 <init> (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 <init> (Lru/mipt/npm/devices/pimotionmaster/VirtualDevice;Lspace/kscience/dataforge/context/Context;)V
public fun close ()V
}

View File

@ -23,7 +23,7 @@ val ktorVersion: String by rootProject.extra
val dataforgeVersion: String by extra val dataforgeVersion: String by extra
dependencies { dependencies {
implementation(project(":controls-ktor-tcp")) implementation(project(":controls-ports-ktor"))
implementation(project(":controls-magix-client")) implementation(projects.controlsMagix)
implementation("no.tornado:tornadofx:1.7.20") implementation("no.tornado:tornadofx:1.7.20")
} }

View File

@ -162,7 +162,7 @@ class PiMotionMasterDevice(
send("STP") send("STP")
} }
val connect by metaAction(descriptorBuilder = { val connect by action(MetaConverter.meta, MetaConverter.unit, descriptorBuilder = {
info = "Connect to specific port and initialize axis" info = "Connect to specific port and initialize axis"
}) { portSpec -> }) { portSpec ->
//Clear current actions if present //Clear current actions if present
@ -171,11 +171,10 @@ class PiMotionMasterDevice(
} }
//Update port //Update port
//address = portSpec.node //address = portSpec.node
port = portFactory(portSpec ?: Meta.EMPTY, context) port = portFactory(portSpec, context)
updateLogical(connected, true) updateLogical(connected, true)
// connector.open() // connector.open()
//Initialize axes //Initialize axes
if (portSpec != null) {
val idn = read(identity) val idn = read(identity)
failIfError { "Can't connect to $portSpec. Error code: $it" } failIfError { "Can't connect to $portSpec. Error code: $it" }
logger.info { "Connected to $idn on $portSpec" } logger.info { "Connected to $idn on $portSpec" }
@ -188,19 +187,16 @@ class PiMotionMasterDevice(
execute(initialize) execute(initialize)
failIfError() failIfError()
} }
null
}
val disconnect by metaAction({ val disconnect by unitAction({
info = "Disconnect the program from the device if it is connected" info = "Disconnect the program from the device if it is connected"
}) { }) {
port?.let{ port?.let {
execute(stop) execute(stop)
it.close() it.close()
} }
port = null port = null
updateLogical(connected, false) updateLogical(connected, false)
null
} }
@ -212,7 +208,7 @@ class PiMotionMasterDevice(
class Axis( class Axis(
val mm: PiMotionMasterDevice, val mm: PiMotionMasterDevice,
val axisId: String val axisId: String,
) : DeviceBySpec<Axis>(Axis, mm.context) { ) : DeviceBySpec<Axis>(Axis, mm.context) {
/** /**
@ -244,7 +240,7 @@ class PiMotionMasterDevice(
private fun axisBooleanProperty( private fun axisBooleanProperty(
command: String, command: String,
descriptorBuilder: PropertyDescriptor.() -> Unit = {} descriptorBuilder: PropertyDescriptor.() -> Unit = {},
) = booleanProperty( ) = booleanProperty(
read = { read = {
readAxisBoolean("$command?") readAxisBoolean("$command?")
@ -257,7 +253,7 @@ class PiMotionMasterDevice(
private fun axisNumberProperty( private fun axisNumberProperty(
command: String, command: String,
descriptorBuilder: PropertyDescriptor.() -> Unit = {} descriptorBuilder: PropertyDescriptor.() -> Unit = {},
) = doubleProperty( ) = doubleProperty(
read = { read = {
mm.requestAndParse("$command?", axisId)[axisId]?.toDoubleOrNull() mm.requestAndParse("$command?", axisId)[axisId]?.toDoubleOrNull()
@ -334,11 +330,11 @@ class PiMotionMasterDevice(
info = "Velocity value for closed-loop operation" info = "Velocity value for closed-loop operation"
} }
val move by metaAction { val move by action(MetaConverter.meta, MetaConverter.unit) {
val target = it.double ?: it?.get("target").double ?: error("Unacceptable target value $it") val target = it.double ?: it["target"].double ?: error("Unacceptable target value $it")
write(closedLoop, true) write(closedLoop, true)
//optionally set velocity //optionally set velocity
it?.get("velocity").double?.let { v -> it["velocity"].double?.let { v ->
write(velocity, v) write(velocity, v)
} }
write(targetPosition, target) write(targetPosition, target)
@ -347,7 +343,6 @@ class PiMotionMasterDevice(
read(position) read(position)
delay(200) delay(200)
} }
null
} }
} }

View File

@ -12,8 +12,8 @@ import tornadofx.*
/** /**
* Bind a FX property to a device property with a given [spec] * Bind a FX property to a device property with a given [spec]
*/ */
fun <D : Device, T : Any> Device.fxProperty( fun <D : Device, T : Any> D.fxProperty(
spec: DevicePropertySpec<D, T> spec: DevicePropertySpec<D, T>,
): ReadOnlyProperty<T> = object : ObjectPropertyBase<T>() { ): ReadOnlyProperty<T> = object : ObjectPropertyBase<T>() {
override fun getBean(): Any = this override fun getBean(): Any = this
override fun getName(): String = spec.name override fun getName(): String = spec.name
@ -21,7 +21,6 @@ fun <D : Device, T : Any> Device.fxProperty(
init { init {
//Read incoming changes //Read incoming changes
onPropertyChange(spec) { onPropertyChange(spec) {
if (it != null) {
runLater { runLater {
try { try {
set(it) set(it)
@ -29,9 +28,6 @@ fun <D : Device, T : Any> Device.fxProperty(
logger.info { "Failed to set property $name to $it" } logger.info { "Failed to set property $name to $it" }
} }
} }
} else {
invalidated()
}
} }
} }
} }
@ -44,7 +40,6 @@ fun <D : Device, T : Any> D.fxProperty(spec: WritableDevicePropertySpec<D, T>):
init { init {
//Read incoming changes //Read incoming changes
onPropertyChange(spec) { onPropertyChange(spec) {
if (it != null) {
runLater { runLater {
try { try {
set(it) set(it)
@ -52,9 +47,6 @@ fun <D : Device, T : Any> D.fxProperty(spec: WritableDevicePropertySpec<D, T>):
logger.info { "Failed to set property $name to $it" } logger.info { "Failed to set property $name to $it" }
} }
} }
} else {
invalidated()
}
} }
onChange { newValue -> onChange { newValue ->

View File

@ -7,4 +7,7 @@ org.gradle.parallel=true
publishing.github=false publishing.github=false
publishing.sonatype=false publishing.sonatype=false
toolsVersion=0.14.6-kotlin-1.8.20 org.gradle.configureondemand=true
org.gradle.jvmargs=-Xmx4096m
toolsVersion=0.14.10-kotlin-1.9.0

View File

@ -1,4 +0,0 @@
# Module magix

View File

@ -1,3 +0,0 @@
subprojects{
}

View File

@ -1,32 +1,23 @@
# Module magix-api # Module magix-api
A kotlin API for magix standard and some zero-dependency magix services
## Usage ## Usage
## Artifact: ## 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`.
**Gradle Groovy:**
```groovy
repositories {
maven { url 'https://repo.kotlin.link' }
mavenCentral()
}
dependencies {
implementation 'space.kscience:magix-api:0.1.1-SNAPSHOT'
}
```
**Gradle Kotlin DSL:** **Gradle Kotlin DSL:**
```kotlin ```kotlin
repositories { repositories {
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")
//uncomment to access development builds
//maven("https://maven.pkg.jetbrains.space/spc/p/sci/dev")
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
implementation("space.kscience:magix-api:0.1.1-SNAPSHOT") implementation("space.kscience:magix-api:0.2.0")
} }
``` ```

View File

@ -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 <init> (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 <init> (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 <init> (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 <init> (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 <init> ()V
public synthetic fun <init> (ILjava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (Ljava/util/Collection;Ljava/util/Collection;Ljava/util/Collection;)V
public synthetic fun <init> (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 <init> (ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (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 <init> (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 <init> (ILjava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (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 <init> (ILjava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (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 <init> (ILjava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (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;
}

View File

@ -1,8 +1,14 @@
import space.kscience.gradle.Maturity
plugins { plugins {
id("space.kscience.gradle.mpp") id("space.kscience.gradle.mpp")
`maven-publish` `maven-publish`
} }
description = """
A kotlin API for magix standard and some zero-dependency magix services
""".trimIndent()
kscience { kscience {
jvm() jvm()
js() js()
@ -13,3 +19,6 @@ kscience {
} }
} }
readme{
maturity = Maturity.EXPERIMENTAL
}

View File

@ -54,3 +54,8 @@ public interface MagixEndpoint {
} }
} }
} }
/**
* An alias for [MagixEndpoint.send]
*/
public suspend fun MagixEndpoint.send(message: MagixMessage): Unit = broadcast(message)

View File

@ -2,8 +2,28 @@ package space.kscience.magix.api
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
/**
* A plugin that could be inserted into basic loop implementation.
*/
public fun interface MagixFlowPlugin { public fun interface MagixFlowPlugin {
public fun start(scope: CoroutineScope, magixFlow: MutableSharedFlow<MagixMessage>): Job
/**
* Attach a [Job] to magix loop.
* Receive messages from [receive].
* Send messages via [sendMessage]
*/
public fun start(
scope: CoroutineScope,
receive: Flow<MagixMessage>,
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<MagixMessage>): Job =
start(scope, magixFlow) { magixFlow.emit(it) }
} }

View File

@ -6,6 +6,11 @@ import kotlinx.serialization.KSerializer
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonElement
import space.kscience.magix.api.MagixEndpoint.Companion.magixJson 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<T>( public data class MagixFormat<T>(
val serializer: KSerializer<T>, val serializer: KSerializer<T>,
val formats: Set<String>, val formats: Set<String>,
@ -13,31 +18,40 @@ public data class MagixFormat<T>(
val defaultFormat: String get() = formats.firstOrNull() ?: "magix" 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 <T> MagixEndpoint.subscribe( public fun <T> MagixEndpoint.subscribe(
format: MagixFormat<T>, format: MagixFormat<T>,
originFilter: Collection<String?>? = null, originFilter: Collection<String>? = null,
targetFilter: Collection<String?>? = null, targetFilter: Collection<String>? = null,
): Flow<Pair<MagixMessage, T>> = subscribe( ): Flow<Pair<MagixMessage, T>> = subscribe(
MagixMessageFilter(format = format.formats, origin = originFilter, target = targetFilter) MagixMessageFilter(format = format.formats, source = originFilter, target = targetFilter)
).map { ).map {
val value: T = magixJson.decodeFromJsonElement(format.serializer, it.payload) val value: T = magixJson.decodeFromJsonElement(format.serializer, it.payload)
it to value it to value
} }
public suspend fun <T> MagixEndpoint.broadcast( /**
* Send a message using given [format] to encode the message payload. The format field is also taken from [format].
*
*/
public suspend fun <T> MagixEndpoint.send(
format: MagixFormat<T>, format: MagixFormat<T>,
payload: T, payload: T,
source: String,
target: String? = null, target: String? = null,
id: String? = null, id: String? = null,
parentId: String? = null, parentId: String? = null,
user: JsonElement? = null, user: JsonElement? = null,
origin: String = format.defaultFormat,
) { ) {
val message = MagixMessage( val message = MagixMessage(
format = format.defaultFormat, format = format.defaultFormat,
payload = magixJson.encodeToJsonElement(format.serializer, payload), payload = magixJson.encodeToJsonElement(format.serializer, payload),
origin = origin, sourceEndpoint = source,
target = target, targetEndpoint = target,
id = id, id = id,
parentId = parentId, parentId = parentId,
user = user user = user

View File

@ -1,7 +1,7 @@
package space.kscience.magix.api package space.kscience.magix.api
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.*
/* /*
@ -27,9 +27,20 @@ import kotlinx.serialization.json.JsonElement
public data class MagixMessage( public data class MagixMessage(
val format: String, val format: String,
val payload: JsonElement, val payload: JsonElement,
val origin: String, val sourceEndpoint: String,
val target: String? = null, val targetEndpoint: String? = null,
val id: String? = null, val id: String? = null,
val parentId: String? = null, val parentId: String? = null,
val user: JsonElement? = null, val user: JsonElement? = null,
) )
/**
* 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"
}

Some files were not shown because too many files have changed in this diff Show More