Introduction to Resources
OGEMA stores all data in terms of so-called Resources, and the set of all Resources forms the nodes of a graph-like structure called the Resource tree. For a thorough introduction to the concept of OGEMA Resources, see Section 1.3 of the Introduction document. From the programmer's perspective, Resources are Java objects, which in addition are stored persistently to the OGEMA database. Each Resource has a Resource Type, which corresponds to its Java class (more precisely, the type is specified as an interface), and a name. Between resources, a parent-child relationship exists, and a resource can reference another resource of a compatible type. Resources that do not posses a parent are called Toplevel Resources. Every resource has a unique location, which is determined by its chain of parents (which is unique, if we ignore references). Besides, resources can be associated a path, which is similar to the location but may include references. If the path of a Resource does not include any references, then it is equal to its location.
The following categories of Resources exist. The types printed in bold below are marker interfaces and cannot be instantiated. Only objects of their inheriting types can be created.The supertype of all Resources is called Resource, which cannot be instantiated either(tbc).
- Complex Resource (does not exist as an interface; just an informal category comprising all container types). This is the generic case. Complex Resources do not directly contain any values, but rather serve as containers for subresources. All complex Resources inherit from one of the following prototypes:
- PhysicalElement. Representing a device, e.g. Thermostat, or CombinedHeatAndPowerGenerator. Special cases of PhysicalElements are Sensors and Actors, which are prototypes themselves.
- Connection. Representing a connection, e.g. ElectricityConnection, or ThermalConnection.
- Data. Represents a data container (with actual values residing within some subresource(s) of type ValueResource, see below), e.g. CalendarEntry or DeviceAddress.
- Configuration. Represents configuration settings. There are no predefined Configuration types, rather this is a marker type for custom configuration resources, which should inherit from Configuration.
- ValueResource; Resources containing actual values. Note that ValueResources may still possess subresources, hence they could be considered as complex Resources at the same time. There are two subcategories of ValueResources, defined by two marker interfaces:
- SingleValueResource. These Resources correspond largely, but not exactly, to the primitive Java variable types. There are BooleanResource, FloatResource, IntegerResource, StringResource, TimeResource, (containing values of type long). In addition, a few PhysicalUnitResource exist, such as PowerResource orTemperatureResource, which extend the generic FloatResource type. The value of a SingleValueResource can be accessed by the getValue() and setValue() methods, which each of the specific types possesses.
- ArrayResource. There are the following subtypes: BooleanArrayResource, ByteArrayResource, FloatArrayResource, IntegerArrayResource, StringArrayResource, TimeArrayResource. The actual values can be accessed through getValues() and setValues() methods.
- Schedule. Schedules are Resources representing time series. There are two types of schedules, which differ only in the interpretation: timestamps in an AbsoluteSchedule represent absolute time (milliseconds since 1st Jan 1970), timestamps of a RelativeSchedule represents time differences. See also the page Time series.
- ResourceList. A list of resources. It takes a generic parameter that specifies the resource type of its elements. Note that a ResourceList is itself a resource, and the elements of the list are simply subresources, hence it is possible to access them via the generic Resource methods. Alternatively, access is possible via ResourceList#getAllElements(), and entries can be added via ResourceList#add().
You can find the available Resource types in the Javadoc, packages org.ogema.core.model.*, and org.ogema.model.*. In addition to the predefined types, custom types can be defined for use by individual applications. Similarly to the predefined types, they must inherit from Resource or any of its subtypes. Only the interface specification has to be provided (tbc). An example for a custom resource type could be:
We assume that you have created an empty OGEMA application. In order to be able to use OGEMA Resources, you need to reference the api- and models-dependencies in the pom.xml file of your application. They are already included in the OGEMA archetypes, so that usually there is nothing to be done. If you want to use custom Resource types defined in some other bundle, you have to add the dependency here as well.
OGEMA services for Resource access
Access to resources is governed by the ResourceManagement (create resources), the ResourceAccess (read and write existing resources), and the ResourcePatternAccess (optional, advanced way to create and read/write resources, which simplifies the handling of resources considerably in more complex settings). Each of these OGEMA services can be obtained from the ApplicationManager, which is the single point of entry for applications to the OGEMA framework. The following expandable code block shows an example of how an OGEMA application may keep track of these objects upon start up:
You will need to add the relevant import statements at the beginning of your class file for the resource types you would like to use. If you are using an IDE like Eclipse or Netbeans, the import statements are usually deduced by the IDE (cf. screenshot), and the user only has to confirm the inclusion.
Figure: Eclipse proposes the correct import for new Resource types when the cursor is placed on the as yet unknown type declaration.
In order to create, delete, read, write, or de-/activate resources, an application needs special permissions.These are explained in the Security Technical Notes, and will not be explicitly mentioned in the instructions below. Instead, we assume that all required permissions are available. For development purposes, one can start the framework with security disabled, or grant all permissions to the own application.
Create and delete Resources
Top-level resources are created through the method ResourceManagement.createResource:
The first argument is a String, which specifies the resource name, which is also equal to the resource path for top-level resources. It must be unique and a valid Java variable name. The second argument is a class specifying the Resource type. If a Resource with the same name and type already exists, the method simply returns the existing Resource. If the existing Resource has an incompatible type, an Exception is thrown.
Subresources of existing Resources are not created by the ResourceManagement. If a subresource is specified in the type definition (interface) of the parent resource, it can be accessed via a Java method, and then created:
Before the create() method is called on a subresource, it is only available as a so-called Virtual Resource, and cannot be used to store any values. An alternative way to create subresources, which is not restricted to subresources specified in the type definition of the parent, is via getSubResource(), plus create():
In order to delete a resource and all of its (non-reference) subresources, simply call its delete() method. If you think you might need to recreate the resource later on, consider deactivating it instead of deletion.
Activate and deactivate Resources
OGEMA Resources can be either active or inactive; for most purposes, inactive Resources should be treated as if they were not present. Newly created OGEMA Resource are always inactive, and they should not be activated before they contain a sensible value (in case of ValueResources), or before all relevant subresources are created (in case of complex Resources). An important convention is
There are a few exceptions to this rule:
- A resource that is only used to triggers some action may be activated before it is written for the first time, and may remain active after the action has been executed. As an example, consider the subresource stateControl of an OnOffSwitch; writing a new boolean value to this resource usually tells the hardware driver in charge to toggle the switch. When the driver creates the resource, it may activate it already, so that potential users know there is a controllable switch in the system. The actual state of the switch is kept in another subresource of the OnOffSwitch, stateFeedback, to which the above rule applies: when the driver cannot determine the current switch state, it should deactivate the resource
- Schedules are another exception to this rule; if you cannot determine the appropriate value for a schedule at a given time, you can give it a Quality.BAD value (see Schedules). No need to deactivate the full schedule.
Hence, the creation of a temperature sensor and some of its subresources could be done as follows:
Inactive resources can be accessed just like active ones, but they do not trigger callbacks (see below). Furthermore, while it is possible to enable value logging for inactive resources, no actual log data will be registered before the resource is activated.
The ResourceAccess can be used to access Resources by path:
or by type:
Use getToplevelResources() instead, to acces only toplevel Resources. Subresources can be accessed directly from the parent:
The first option is the canonical one for subresources available in the type definition of the parent, and it is the most convenient one, because it is supported by tab completion of the tpyical IDEs. The last option also works for resources that are not declared in the type definition of the parent (so-called "Decorators"). Direct access returns all matching resources, irrespectively of whether they are active or inactive.
Resource Demand Listeners
If you need to get informed about new Resources of a specific type, and about disappearing ones, you should register a ResourceDemandListener, using the ResourceAccess.addResourceDemand() method. In this case, the two methods resourceAvailable() and resourceUnavailable() of the listener have to be implemented. A basic example is presented in the code box below. Resource demands only inform you about new active resources, inactive ones are ignored until they get activated.
For most realistic applications, it is not sufficient to get informed about appearance and disappearance of individual resources. For instance, if you are developing a light control application, you will be interested not only in resources of type ElectricLight, but also whether the lights you get are actually controllable. This information is indicated in a subresource ElectricLight.onOffSwitch().controllable(). The pedestrian way to handle this information would be to register a ResourceStructureListener for the controllable() resource whenever a light becomes available, so that you will be informed if the required subresource becomes available, and a ResourceValueListener listener whenever the controllable() subresource becomes available, so that you will be notified of its value changes. As the example below shows, things quickly get very complex.
In order to reduce this complexity, the concept of PatternListeners has been introduced.
A PatternListener comprises the concepts of ResourceDemandListeners, ResourceStructureListeners and ResourceValueListeners, and thus makes them redundant for most applications:
PatternListeners are registered on ResourcePatterns, which specify a main Resource type to listen for (like ResourceDemandListeners), plus a couple of filters based on subresources of the main type. They are explained in detail in Advanced Resource Access (Resource Patterns).
Reading and Writing Resource values
TODO: explain AccessModes.
They possess a getValue() and a setValue() method. Some special types (subtypes of PhysicalUnitResource) have additional get and set methods for particular units. For instance, the TemperatureResource type has methods
setKelvin(). The ordinary getValue and setValue() methods are specified to provide and assume values in units of Kelvin in this case, so coincide with getKelvin() and setKelvin().
ArrayResources possess a getValues() and a setValues() method.
We have getValues() with parameters either start and end time, or only start time, and getValue(long timeStamp). Time stamps are in milliseconds since January 1st, 1970.The values returned are of type SampledValue, and we need to infer which type of values to expect from the type of the parent resource, to extract the actual values. If, for instance, the parent resource is a FloatResource, then we could obtain the schedule values in a certain time interval as follows:
In line 4 we filter out values of bad quality. In order to write Schedule values, we need to create a SampledValue object for each value. Here is an example:
In the fist line, am is the ApplicationManager. The method getFrameworkTime wraps the System.currentTimeMillis() method; it allows to simulate a faster elapsing of time, and hence should be used by all OGEMA applications instead of the System methods.
Multiple Resource operations (read and/or write) can be executed in a single atomic action, by using a so-called ResourceTransaction. This way, it is guaranteed that no other app performs any operations on the resource tree in parallel, which could lead to ill-defined states, and worse. See the page Resource Transactions.