Doc #4
54
README.md
54
README.md
@ -1,4 +1,56 @@
|
|||||||
[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
|
[![JetBrains Research](https://jb.gg/badges/research.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
|
||||||
|
|
||||||
# DataForge-control
|
# 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)
|
||||||
|
@ -6,6 +6,9 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.io.Closeable
|
import kotlinx.io.Closeable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* General interface describing a managed Device
|
||||||
|
*/
|
||||||
interface Device: Closeable {
|
interface Device: Closeable {
|
||||||
/**
|
/**
|
||||||
* List of supported property descriptors
|
* List of supported property descriptors
|
||||||
@ -13,7 +16,8 @@ interface Device: Closeable {
|
|||||||
val propertyDescriptors: Collection<PropertyDescriptor>
|
val propertyDescriptors: Collection<PropertyDescriptor>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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<ActionDescriptor>
|
val actionDescriptors: Collection<ActionDescriptor>
|
||||||
|
|
||||||
@ -30,12 +34,13 @@ interface Device: Closeable {
|
|||||||
fun registerListener(listener: PropertyChangeListener, owner: Any? = listener)
|
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<*>
|
suspend fun getProperty(propertyName: String): MetaItem<*>
|
||||||
|
|
||||||
@ -51,8 +56,8 @@ interface Device: Closeable {
|
|||||||
suspend fun setProperty(propertyName: String, value: MetaItem<*>)
|
suspend fun setProperty(propertyName: String, value: MetaItem<*>)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a request and suspend caller while request is being processed.
|
* Send an action request and suspend caller while request is being processed.
|
||||||
* Could return null if request does not return meaningful answer.
|
* Could return null if request does not return a meaningful answer.
|
||||||
*/
|
*/
|
||||||
suspend fun call(action: String, argument: MetaItem<*>? = null): MetaItem<*>?
|
suspend fun call(action: String, argument: MetaItem<*>? = null): MetaItem<*>?
|
||||||
|
|
||||||
|
@ -18,6 +18,6 @@ suspend fun DeviceHub.setProperty(deviceName: String, propertyName: String, valu
|
|||||||
.setProperty(propertyName, value)
|
.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"))
|
(getDevice(deviceName) ?: error("Device with name $deviceName not found in the hub"))
|
||||||
.call(command, argument)
|
.call(command, argument)
|
@ -2,6 +2,10 @@ package hep.dataforge.control.api
|
|||||||
|
|
||||||
import hep.dataforge.meta.MetaItem
|
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 {
|
interface PropertyChangeListener {
|
||||||
fun propertyChanged(propertyName: String, value: MetaItem<*>?)
|
fun propertyChanged(propertyName: String, value: MetaItem<*>?)
|
||||||
}
|
}
|
@ -6,6 +6,9 @@ import hep.dataforge.control.api.PropertyChangeListener
|
|||||||
import hep.dataforge.control.api.PropertyDescriptor
|
import hep.dataforge.control.api.PropertyDescriptor
|
||||||
import hep.dataforge.meta.MetaItem
|
import hep.dataforge.meta.MetaItem
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baseline implementation of [Device] interface
|
||||||
|
*/
|
||||||
abstract class DeviceBase : Device {
|
abstract class DeviceBase : Device {
|
||||||
private val properties = HashMap<String, ReadOnlyDeviceProperty>()
|
private val properties = HashMap<String, ReadOnlyDeviceProperty>()
|
||||||
private val actions = HashMap<String, Action>()
|
private val actions = HashMap<String, Action>()
|
||||||
@ -16,7 +19,7 @@ abstract class DeviceBase : Device {
|
|||||||
listeners.add(owner to listener)
|
listeners.add(owner to listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun removeListener(owner: Any?) {
|
override fun removeListeners(owner: Any?) {
|
||||||
listeners.removeAll { it.first == owner }
|
listeners.removeAll { it.first == owner }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package hep.dataforge.control.controlers
|
package hep.dataforge.control.controllers
|
||||||
|
|
||||||
import hep.dataforge.meta.*
|
import hep.dataforge.meta.*
|
||||||
import hep.dataforge.names.asName
|
import hep.dataforge.names.asName
|
@ -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.Device
|
||||||
import hep.dataforge.control.api.PropertyChangeListener
|
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.Envelope
|
||||||
import hep.dataforge.io.Responder
|
import hep.dataforge.io.Responder
|
||||||
import hep.dataforge.io.SimpleEnvelope
|
import hep.dataforge.io.SimpleEnvelope
|
@ -1,4 +1,4 @@
|
|||||||
package hep.dataforge.control.controlers
|
package hep.dataforge.control.controllers
|
||||||
|
|
||||||
import hep.dataforge.io.Envelope
|
import hep.dataforge.io.Envelope
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
@ -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.Device
|
||||||
import hep.dataforge.control.api.PropertyChangeListener
|
import hep.dataforge.control.api.PropertyChangeListener
|
||||||
@ -23,6 +23,6 @@ suspend fun Device.flowValues(): Flow<Pair<String, MetaItem<*>>> = callbackFlow
|
|||||||
}
|
}
|
||||||
registerListener(listener)
|
registerListener(listener)
|
||||||
awaitClose {
|
awaitClose {
|
||||||
removeListener(listener)
|
removeListeners(listener)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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.DeviceProperty
|
||||||
import hep.dataforge.control.base.ReadOnlyDeviceProperty
|
import hep.dataforge.control.base.ReadOnlyDeviceProperty
|
@ -1,7 +1,7 @@
|
|||||||
package hep.dataforge.control.demo
|
package hep.dataforge.control.demo
|
||||||
|
|
||||||
import hep.dataforge.control.base.*
|
import hep.dataforge.control.base.*
|
||||||
import hep.dataforge.control.controlers.double
|
import hep.dataforge.control.controllers.double
|
||||||
import hep.dataforge.values.asValue
|
import hep.dataforge.values.asValue
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
BIN
docs/pictures/demo-view.png
Normal file
BIN
docs/pictures/demo-view.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
Loading…
Reference in New Issue
Block a user