eVOLVER concepts
The eVOLVER system is made up of several configurable components tied together by a manager component that orchestrates the application control loop and exposure to the web interface. Components can be grouped into two high level categories:
Hardware and experiment control components: These are the main target of extensions, representing the physical devices and experiment logic reading from and controlling them.
HardwareDriver, specifically sensors that can be read (SensorDriver), and effectors that can be controlled (EffectorDriver).Controllerwhich encompass the experiment logic.
Included in the package are implementations for a variety of hardware and controllers.
Application layer components: Components that manage the control loop, provide historical data, logging and interfacing to the web. These are configurable and can be extended to add new functionality, however modifying them should not be a prerequisite for adding new hardware or experiment logic. These include:
Evolverwhich orchestrates the control loop.Historywhich provides historical data.
Included in the package are standard implementations of all of these components.
Configuration and ConfigDescriptors
All components in the eVOLVER can be represented by a so-called
ConfigDescriptor. This is the key
component which translates from a static serialized configuration (e.g. that
which can be represented in a yaml file) to the in-memory instantiated objects
they describe. The ConfigDescriptor also enables the system to be extended
with pluggable modules for new hardware, controllers and other components
described above.
As an example, the serialized version - in the evolver.yml file - of a particular hardware might look like:
hardware:
temp:
classinfo: "evolver.hardware.standard.od_sensor.ODSensor"
config:
addr: "od90"
integrations: 500
The object under hardware.temp is recognized in the eVOLVER system as a
ConfigDescriptor, where the object to create is an
evolver.hardware.standard.od_sensor.ODSensor and the config to pass in will
be an evaluated version of its Config. To clarify the point, consider the
following abbreviated definition of the ODSensor class:
class ODSensor(SensorDriver):
class Config(SensorDriver.Config):
addr: str
integrations: int = 500
As related to the above yaml configuration. We can see the top-level class
ODSensor represented in classinfo and the config values as modeled in
the Config class represented within the config key.
Experiment loop
In the normal mode, the eVOLVER operates in a loop, continuously performing the following steps in succession:
Read sensors: values are read and buffered within individual hardware drivers, for all configured sensors. For more on reading sensors and buffering see Buffering.
Execute controllers: the control method of controllers is called for all controllers, in sequence. The controllers can get sensor values that have previously been read, and set effector values.
Commit effector values: effector values are commit`ted for all configured effectors. This applies the changes `set in the previous step to the underlying hardware. For more on why this is done in two steps see Buffering.
These activities are coordinated in the
Evolver class, via the
loop_once method, and executed
continuously within the application. Configuration option enable_control
controls whether the control (executing the control method of Controllers) and
commit steps (executing the commit method of Effectors) are executed during
the loop.
Buffering
Both Sensor and Effector drivers in the eVOLVER system have separate methods for reading/writing values to the underlying hardware device and for getting and setting values in within the eVOLVER software framework.
The primary reason for this separation is to simplify the operation of potentially multiple controllers working against multiple vials, while recognizing that the serial protocol for hardware on the standard eVOLVER boxes both:
reads-to/writes-from all vials in a single serial communication, and
incurs a significant latency overhead for each call that would prohibit making a large number of hardware read calls within a single loop.
The following advice should be taken then, depending on where in the system development is taking place:
In Experiment code
For a developer of a Controller, this means that:
reads of sensor values should be done using the
getmethod of the SensorDriver interface, which will return the most recent value read from the read phase of the current loop.writes of effector values should be done using the
setmethod of the EffectorDriver interface, which will buffer the value to be committed in the commit phase of the current loop.
These methods can be called as many times as required with no additional penalty, simplifying the controller code, for example when looping over the set of configured vials.
Note
Note that values are typically neither read nor committed within the control
method itself - these are executed by the eVOLVER in the read and
commit phases of Experiment loop. Multiple get calls would
return the same values and subsequent set calls would overwrite the value
to commit.
While technically it is feasible to call the read and commit methods within a controller, we recommend against doing so. Due to the serial communication latency, and the fact that a commit will be done for all hardware by the system at the commit step, it is recommended that loop activity acts and submits proposals for only single value read-out.
In Hardware code
For a developer of a SensorDriver or EffectorDriver, this means that:
reads of sensor values should be done using the
readmethod of the SensorDriver interface, which must read the value from the underlying hardware device.writes of effector values should be done using the
commitmethod of the EffectorDriver interface, which must write the value to the underlying hardware device.