Core concepts & terminology
This chapter describes some of the core concepts and commonly used terms in OpenEMS:
OpenEMS Edge is using the OSGi platform to provide a completely modular and dynamic service oriented system.
Logical groups of source code are put into one OSGi Bundle. Every directory in the source code root directory starting with
io.openems.*` is a bundle.
More bundle naming conventions are:
Bundle names ending with
*.commonare for common code that is shared by multiple components. Examples are:
for common code between Edge and Backend, like helper utils, JSON-RPC definitions and Abstract Worker implementations
for common code within Backend.
for common code within Edge.
for common code used by API Controllers
Bundle names ending with
*.apiare for Natures and APIs. Examples are:
Bundle names ending with
*.coreare for shared and helper OSGi services. Examples are:
for central singleton services:
handles access to OpenEMS Components and Channels
is responsible for the process Cycle
for accessing the host and operating system
for some Meta information like the version of the running OpenEMS Edge
for summing the values of the entire energy system, all meters, energy storage systems and so on.
for the central EssPower service, that distributes power requirements to different energy storage systems
An OpenEMS Component is the fundamental building block in OpenEMS. Within the used OSGi Java framework, an OpenEMS component represents a service with requirements and capabilities.
As an example, an OpenEMS Component can declare to have the capabilities of an Energy Storage System (ESS) and as such represents the digital twin of a real device. A specific control algorithm can be implemented as a separate OpenEMS Component that declares a requirement for an ESS. Using this metadata, these building blocks are wired together at runtime and form a very flexible system. OSGi provides the capability to enable, modify or disable an OpenEMS Component at any time, without requiring a restart of the software. Re-wiring of the building blocks happens transparently in the background by the framework.
Every OpenEMS Component is identified by a unique ID, the "Component-ID".
In an ecosystem consisting of a couple of ESS, a power meter at the grid connection point, and a measured photovoltaic system, those Component-IDs can be represented as follows:
ess1 for the first ESS
ess2 for the second ESS
ess0 for a virtual ESS cluster Component that aggregates ess1 and ess2
meter0 for the power meter at the grid connection point
meter1 for the measured photovoltaic system
To declare an OpenEMS component, the Java class has to
implement the OpenemsComponent interface .
Each OpenEMS component has a defined set of data points.
These data points are called "Channels".
Each represents a single piece of information about a component.
By definition, each channel has a unique ID, the "Channel-ID", within its parent component.
Channels are defined by metadata like descriptive text, access-mode (
write-only), data type (
float, etc.), and unit of measure (
Degree Celsius, etc.).
It is up to the OpenEMS component to provide the input for its read-channels as well as triggering actions on write-channels.
Example: An OpenEMS Component that represents a device connected via the Modbus communication protocol continuously reads data, such as the current measured power and provides the data in its Channels. Other Components in the system can then use the channel data for their application, e.g. as input for a control algorithm, to analyse it, store it locally or publish it via an application programming interface (API).
An energy system architecture as depicted in the Introduction is complex: connected to multiple hardware devices - batteries, converters, meters, and others - and an operating system and other software components.
All of these elements are possible sources of errors.
Because of this, measures are implemented in OpenEMS to improve fault tolerance.
The developer needs to be aware, that every Channel value, while it will never change within a cycle, it could always be
null, e.g. because there is no communication (yet) with the external hardware device or service.
Therefore, the programming API for accessing a channel value requires an explicit declaration of what should be done in that case.
It provides the following methods to get the actual value:
public T getOrError() throws InvalidValueException;
public T orElse(T alternativeValue);
Each Channel implements the Channel interface .
Certain categories of devices and services provide the same kind of information (i.e. Channels). To group these similar devices and services, OpenEMS defines "Natures" as sets of characteristics and attributes which need to be provided by each component that implements them. That is, a Nature extends a normal Java interface with channels.
Examples of abstracting physical devices using Natures are: - "SymmetricMeter" for power meters - "SymmetricEss" for symmetric battery energy storage systems - "Evcs" for electric vehicle charging stations.
OpenEMS components can declare their service capabilities and requirements as Natures. In this way, a control algorithm can simply declare a requirement for a controllable energy storage system (“ManagedSymmetricEss”) and will at runtime be wired with a service that provides this capability. The control algorithm does not need to know anything about the ESS’s specific communication interface, protocol, or manufacturer.
Natures extend normal Java interfaces with 'Channels'.
If a Component implements a Nature it also needs to provide the required Channels.
For example the Energy Storage System (ESS) Simulator Simulator.EssSymmetric.Reacting implements the Ess interface and therefor needs to provide a
Soc Channel that provides the current 'State of Charge' of the battery.
Controllers are written against Nature implementations. Example: A Controller can be used with any ESS, because it can be sure that it provides all the data the Controller requires for its algorithm.
By combining the unique Component-ID and Channel-ID each Channel in the system can be addressed by a distinct 'Channel Address' in the form
Example: the state of charge ("Soc") of the first energy storage system ("ess0") has the channel address
The Scheduler handles the order, in which Controllers are executed. For details see Scheduler and Controller below.
The actual business logic or algorithms are wrapped as 'Controllers'. i.e. they implement the Controller interface . Each Controller holds one specific, encapsulated task.