Controls-kt is built on top of the DataForge framework. Understanding core DataForge concepts is essential for working with Controls-kt.
The Context is the central environment for any DataForge-based application. It acts as both a service locator and a lifecycle manager.
- Service Locator: All shared components (Plugins) are registered in a
Context. You can request a plugin from the context, and if it's not present, it will be created and installed. - Lifecycle Management: Every
Contexthas an associatedCoroutineScope. When the context is closed, all jobs running in its scope are cancelled. This ensures that devices and background tasks are properly cleaned up. - Hierarchical: Contexts can have parents, allowing for inherited services and configuration.
Example of creating a context with DeviceManager:
val context = Context("MyContext") {
plugin(DeviceManager)
}A Plugin is a modular unit of functionality that can be attached to a Context. In Controls-kt, most high-level features are implemented as plugins.
- Modularity: Plugins allow keeping the core library small while providing optional features like Modbus support, Magix integration, or specialized port implementations.
- Dependency Management: Plugins can depend on other plugins. For example, many
Controls-ktfeatures depend on theDeviceManagerplugin. - Configuration: Plugins can be configured using
Metaduring installation.
Commonly used plugins in Controls-kt:
DeviceManager: Manages the lifecycle and registration of devices.Ports: Provides a registry for communication ports.Magix: Integrates devices with the Magix event bus.
Name is a hierarchical identifier used to address devices, properties, and metadata values. It is conceptually similar to a file system path but optimized for performance and type safety.
- Hierarchical Structure: A
Nameconsists of multipleNameTokens. In string representation, these tokens are usually separated by dots (e.g.,room1.sensor2.temperature). - Device Addressing: In a
DeviceTree, each device has aNamerelative to the tree root. - Meta Navigation:
Nameis used to navigate through nestedMetastructures (DataForge's tree-like data format). - Type Safety: The
Nameclass provides methods for manipulating paths safely, such as+for concatenation,cutFirst(),first(), andparent.
Example of using Name:
val deviceName = "sensors.thermal.main".parseAsName()
val propertyPath = deviceName + "value" // sensors.thermal.main.value
println(deviceName.first()) // sensors
println(deviceName.cutFirst()) // thermal.mainMeta is the universal data format used by DataForge and Controls-kt. It is a tree-like structure that can represent scalars, lists, and nested maps.
- Versatility: It can be used for configuration, device state, message payloads, and more.
- Serialization:
Metacan be easily converted to and from JSON, CBOR, or other formats. - Type Safety: While
Metaitself is dynamic, DataForge providesMetaConverterto bridge betweenMetaand typed Kotlin objects. - Observation:
MutableMetaallows observing changes to specific values within the tree.
In Controls-kt, property values and action arguments are almost always represented as Meta.