4. Plugin development — ESIBD Explorer 0.6 documentation

# 4. Plugin development

If you are a user of ESIBD Explorer you can skip this section. Please read on if you want to extend the functionality by adding plugins or adapt the software for a different type of experiment.

The main code does not need to be modified to add support for additional devices and other features. Instead, you can add `Plugin` that will be automatically integrated into the user interface. In fact, everything the user sees in ESIBD Explorer is a `Plugin`. This section will discuss the underlying framework that enables implementation of and communication between different `devices`, `scans`, and other `plugins`.

To add plugins, all you need to do is prepare a plugin file inside a sub folder of the user defined plugin path. A plugin file is a python script that defines a `providePlugins()` function, which returns one or multiple `plugins`. `Plugins` can be enabled in the plugin dialog found in Settings after restarting the software. It is recommended that your custom plugin classes inherit directly from `Plugin`, `Device`, `Scan`, or from one of the other built-in plugins. All built-in `plugins` can be imported from `esibd.plugins` and the corresponding modules in the plugins_internal folder. Many other helpful classes and methods can be imported from `esibd.core`.

If you need to extend the internal plugins just give them another name and make sure to unselect the original one in the plugin manager dialog. Core plugins, like Explorer, Settings, Console, Browser, etc., can also be extended using extended.py and providePlugins.py.

If you want to do something completely different to the already implemented functionality, get in touch and see if we can implement a base class that can be reused for similar projects in the future and keeps your custom plugin code minimal.

## 4.1. Plugin

`Plugins` abstract basic GUI code for devices, scans, and other high level UI elements. All plugins are ultimately derived from the `Plugin` class. The doc string of the plugin class will be shown in the corresponding help window unless documentation is implemented explicitly.

## 4.2. PluginManager

The `PluginManager` is responsible for loading all internal and external Plugins. It catches errors or incompatibilities while loading, initializing, and closing plugins. Users will only see the plugin selection interface accessed from the Settings plugin. The `PluginManager` can be accessed from the Console as PluginManager. It allows plugins to interact by using unique plugin names as attributes, e.g. self.pluginManager.ISEG or self.pluginManager.DeviceManager.

## 4.3. Devices

`Devices` are used to handle communication with one or more physical devices, provide controls to configure the device and display live or previously recorded data. There are input devices (sending input from the user to hardware) and output devices (reading outputs from hardware). Note that some input devices may also read back data from hardware to confirm that the user defined values are applied correctly.

The main interface consists of a list of Channels. By default only the physically relevant information is shown. By entering the advanced mode, additional channel parameters can be configured. The configuration can be exported and imported, though once all channels have been setup it is sufficient to only load values which can be done using a file dialog or from the context menu of an appropriate file in the Explorer. After loading the configurations or values, a change log will be available in the Text plugin to quickly identify what has changed. Each device also comes with a display and a live display. The current values can also be plotted to get a quick overview and identify any unusual values.

## 4.4. Channels

A `channel` represents a virtual or real parameter and manages all data and metadata related to that parameter. Each device can only have one type of channel, but channels have dynamic interfaces that allow to account for differences in the physical backend.

Channels provide a consistent and structured interface to inputs and outputs. In the advanced mode, channels can be duplicated, moved, or deleted. You may also edit channels directly in the corresponding .ini file in the config path.

Channels are accessible from any plugin using `getChannelByName()`. This, and other features like linking channels by equations, depends on the usage of unique and descriptive channel names. Please avoid using very short names, and names that are a subset of names of other channels.

## 4.5. Scan

`Scans` are all sort of measurements that record any number of outputs as a function of any number of inputs. The main interface consists of a list of scan settings. Each scan comes with a tailored display optimized for its specific data format. Scan settings can be imported and exported from the scan toolbar, though in most cases it will be sufficient to import them from the context menu of a previously saved scan file in the Explorer. When all settings are defined and all relevant channels are communicating the scan can be started. A scan can be stopped at any time. At the end of a scan the corresponding file will be saved to the session path. The filename is displayed inside the corresponding graph to allow to find the file later based on exported figures. Scan files are saved in the widely used HDF5 file format that allows to keep data and metadata together in a structured binary file. External viewers, such as HDFView, or minimal python scripts based on the h5py package can be used if files need to be accessed externally. Use the context menu of a scan file to create a template plot file using h5py and adjust it to your needs.

## 4.6. DeviceController

Each `Device` or `Channel` comes with a `DeviceController`. The `DeviceController` is not itself a `Plugin`. It only abstracts the direct hardware communication from `plugins` allowing them to use minimal and consistent code that can be adjusted and reused independently of the hardware. It should do all resource or time intensive communication work in parallel threads to keep the GUI responsive. Following the producer-consumer pattern, the `DeviceController` reads values from a physical device and assigns them to the corresponding `Channel`. The `devices` will collect data from the `Channel` independently. In case you work with time sensitive experiments this concept will need to be adapted. Feel free to use the basic functionality provided by `DeviceController` or implement your own from scratch. As the `DeviceController` only interacts with your custom `Channel` or `Device`, there are no general requirements for its implementation.