diff --git a/README.md b/README.md index 9dbfe11..cb3a920 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,56 @@ [![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub) # DataForge-control -Data acquisition framework based on DataForge + +DataForge-control is a data acquisition framework (work in progress). It is +based on DataForge, a software framework for automated data processing. +This repository contains a prototype of API and simple implementation +of a slow control system, including a demo. + +DataForge-control uses some concepts and modules of DataForge, +such as `Meta` (immutable tree-like structure) and `MetaItem` (which +includes a scalar value, or a tree of values, easily convertable to/from JSON +if needed). + +To learn more about DataForge, please consult the following URLs: + * [Kotlin multiplatform implementation of DataForge](https://github.com/mipt-npm/dataforge-core) + * [DataForge documentation](http://npm.mipt.ru/dataforge/) + * [Original implementation of DataForge](https://bitbucket.org/Altavir/dataforge/src/default/) + +DataForge-control is a [Kotlin-multiplatform](https://kotlinlang.org/docs/reference/multiplatform.html) +application. Asynchronous operations are implemented with +[kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) library. + + +### Features +Among other things, you can: +- Describe devices and their properties. +- Collect data from devices and execute arbitrary actions supported by a device. +- Property values can be cached in the system and requested from devices as needed, asynchronously. +- Connect devices to event bus via bidirectional message flows. + +### `dataforge-control-core` module packages + +- `api` - defines API for device management. The main class here is +[`Device`](dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/Device.kt). +Generally, a Device has Properties that can be read and written. Also, some Actions +can optionally be applied on a device (may or may not affect properties). + +- `base` - contains baseline `Device` implementation +[`DeviceBase`](dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/base/DeviceBase.kt) +and property implementation, including property asynchronous flows. + +- `controllers` - implements Message Controller that can be attached to the event bus, Message +and Property flows. + +### `demo` module + +The demo includes a simple mock device with a few properties changing as `sin` and `cos` of +the current time. The device is configurable via a simple TornadoFX-based control panel. +You can run a demo by executing `application/run` Gradle task. + +The graphs are displayed using [plotly.kt](https://github.com/mipt-npm/plotly.kt) library. + +Example view of a demo: + +![](docs/pictures/demo-view.png) diff --git a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/Device.kt b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/Device.kt index dd37b36..9064b45 100644 --- a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/Device.kt +++ b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/Device.kt @@ -6,6 +6,9 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.io.Closeable +/** + * General interface describing a managed Device + */ interface Device: Closeable { /** * List of supported property descriptors @@ -13,7 +16,8 @@ interface Device: Closeable { val propertyDescriptors: Collection /** - * List of supported requests descriptors + * List of supported action descriptors. Action is a request to the device that + * may or may not change the properties */ val actionDescriptors: Collection @@ -30,12 +34,13 @@ interface Device: Closeable { fun registerListener(listener: PropertyChangeListener, owner: Any? = listener) /** - * Remove all listeners belonging to specified owner + * Remove all listeners belonging to the specified owner */ - fun removeListener(owner: Any?) + fun removeListeners(owner: Any?) /** - * Get the value of the property or throw error if property in not defined. Suspend if property value is not available + * Get the value of the property or throw error if property in not defined. + * Suspend if property value is not available */ suspend fun getProperty(propertyName: String): MetaItem<*> @@ -51,8 +56,8 @@ interface Device: Closeable { suspend fun setProperty(propertyName: String, value: MetaItem<*>) /** - * Send a request and suspend caller while request is being processed. - * Could return null if request does not return meaningful answer. + * Send an action request and suspend caller while request is being processed. + * Could return null if request does not return a meaningful answer. */ suspend fun call(action: String, argument: MetaItem<*>? = null): MetaItem<*>? diff --git a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/DeviceHub.kt b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/DeviceHub.kt index 9e285b6..46958c2 100644 --- a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/DeviceHub.kt +++ b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/DeviceHub.kt @@ -18,6 +18,6 @@ suspend fun DeviceHub.setProperty(deviceName: String, propertyName: String, valu .setProperty(propertyName, value) } -suspend fun DeviceHub.request(deviceName: String, command: String, argument: MetaItem<*>?): MetaItem<*>? = +suspend fun DeviceHub.call(deviceName: String, command: String, argument: MetaItem<*>?): MetaItem<*>? = (getDevice(deviceName) ?: error("Device with name $deviceName not found in the hub")) .call(command, argument) \ No newline at end of file diff --git a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/PropertyChangeListener.kt b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/PropertyChangeListener.kt index 25f3301..f95b072 100644 --- a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/PropertyChangeListener.kt +++ b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/api/PropertyChangeListener.kt @@ -2,6 +2,10 @@ package hep.dataforge.control.api import hep.dataforge.meta.MetaItem +/** + * PropertyChangeListener Interface + * [value] is a new value that property has after a change; null is for invalid state. + */ interface PropertyChangeListener { fun propertyChanged(propertyName: String, value: MetaItem<*>?) } \ No newline at end of file diff --git a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/base/DeviceBase.kt b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/base/DeviceBase.kt index 85f4fa4..f1dac27 100644 --- a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/base/DeviceBase.kt +++ b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/base/DeviceBase.kt @@ -6,6 +6,9 @@ import hep.dataforge.control.api.PropertyChangeListener import hep.dataforge.control.api.PropertyDescriptor import hep.dataforge.meta.MetaItem +/** + * Baseline implementation of [Device] interface + */ abstract class DeviceBase : Device { private val properties = HashMap() private val actions = HashMap() @@ -16,7 +19,7 @@ abstract class DeviceBase : Device { listeners.add(owner to listener) } - override fun removeListener(owner: Any?) { + override fun removeListeners(owner: Any?) { listeners.removeAll { it.first == owner } } diff --git a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/DeviceMessage.kt b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/DeviceMessage.kt similarity index 97% rename from dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/DeviceMessage.kt rename to dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/DeviceMessage.kt index 53e7c74..1ee5717 100644 --- a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/DeviceMessage.kt +++ b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/DeviceMessage.kt @@ -1,4 +1,4 @@ -package hep.dataforge.control.controlers +package hep.dataforge.control.controllers import hep.dataforge.meta.* import hep.dataforge.names.asName diff --git a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/MessageController.kt b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/MessageController.kt similarity index 96% rename from dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/MessageController.kt rename to dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/MessageController.kt index 778e337..f2f1b34 100644 --- a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/MessageController.kt +++ b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/MessageController.kt @@ -1,8 +1,8 @@ -package hep.dataforge.control.controlers +package hep.dataforge.control.controllers import hep.dataforge.control.api.Device import hep.dataforge.control.api.PropertyChangeListener -import hep.dataforge.control.controlers.DevicePropertyMessage.Companion.PROPERTY_CHANGED_ACTION +import hep.dataforge.control.controllers.DevicePropertyMessage.Companion.PROPERTY_CHANGED_ACTION import hep.dataforge.io.Envelope import hep.dataforge.io.Responder import hep.dataforge.io.SimpleEnvelope diff --git a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/MessageFlow.kt b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/MessageFlow.kt similarity index 97% rename from dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/MessageFlow.kt rename to dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/MessageFlow.kt index aec564d..02ecbd6 100644 --- a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/MessageFlow.kt +++ b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/MessageFlow.kt @@ -1,4 +1,4 @@ -package hep.dataforge.control.controlers +package hep.dataforge.control.controllers import hep.dataforge.io.Envelope import kotlinx.coroutines.CoroutineScope diff --git a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/PropertyFlow.kt b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/PropertyFlow.kt similarity index 91% rename from dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/PropertyFlow.kt rename to dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/PropertyFlow.kt index dbfa448..9b2cfc1 100644 --- a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/PropertyFlow.kt +++ b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/PropertyFlow.kt @@ -1,4 +1,4 @@ -package hep.dataforge.control.controlers +package hep.dataforge.control.controllers import hep.dataforge.control.api.Device import hep.dataforge.control.api.PropertyChangeListener @@ -23,6 +23,6 @@ suspend fun Device.flowValues(): Flow>> = callbackFlow } registerListener(listener) awaitClose { - removeListener(listener) + removeListeners(listener) } } \ No newline at end of file diff --git a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/delegateMappers.kt b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/delegateMappers.kt similarity index 91% rename from dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/delegateMappers.kt rename to dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/delegateMappers.kt index 1827d94..d80ceeb 100644 --- a/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controlers/delegateMappers.kt +++ b/dataforge-control-core/src/commonMain/kotlin/hep/dataforge/control/controllers/delegateMappers.kt @@ -1,4 +1,4 @@ -package hep.dataforge.control.controlers +package hep.dataforge.control.controllers import hep.dataforge.control.base.DeviceProperty import hep.dataforge.control.base.ReadOnlyDeviceProperty diff --git a/demo/src/main/kotlin/hep/dataforge/control/demo/DemoDevice.kt b/demo/src/main/kotlin/hep/dataforge/control/demo/DemoDevice.kt index e7b3c7e..8a0f9f2 100644 --- a/demo/src/main/kotlin/hep/dataforge/control/demo/DemoDevice.kt +++ b/demo/src/main/kotlin/hep/dataforge/control/demo/DemoDevice.kt @@ -1,7 +1,7 @@ package hep.dataforge.control.demo import hep.dataforge.control.base.* -import hep.dataforge.control.controlers.double +import hep.dataforge.control.controllers.double import hep.dataforge.values.asValue import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.GlobalScope diff --git a/docs/pictures/demo-view.png b/docs/pictures/demo-view.png new file mode 100644 index 0000000..b7df1eb Binary files /dev/null and b/docs/pictures/demo-view.png differ