This app illustrates the use of listeners and Resource patterns to interact with the database. The app finds out all sensor values on the systems and prints out the number of available sensors every time a new one is available or disappears. In the GUI presented below the app allows to choose a sensor and to view the current sensor reading value.

Create the app

Proceed as described for the Hello World app: create a new app named "sensor-app", without a GUI, but keep the file ending on Controller. You may delete the package ending on "config" and the references to it in the Controller, though (delete the entire method initConfigurationResource and the other references). 

Declare a Resource pattern

Now create a new pattern in the app: right-click in the Package Explorer on the project -> OGEMA -> Add OGEMA Pattern:.

OGEMA_SubMenu.png

Provide the following information:

SensorPatternCreate.png

Go into the newly created SensorPattern class (in the package ending on pattern) and replace the standard entry for model.name() by a reference to the sensor reading value: model.reading. The SensorPattern class will have to look as follows:

import org.ogema.core.model.Resource;
import org.ogema.core.model.ValueResource;
import org.ogema.core.resourcemanager.pattern.ResourcePattern;
import org.ogema.model.sensors.Sensor;

public class SensorPattern extends ResourcePattern<Sensor> {

   /**
     * Only those sensors match the pattern declaration, whose "reading"-subresource
     * exists and is active.
     */

   @Existence(required=CreateMode.MUST_EXIST)
   public final ValueResource reading = model.reading();

   /**
     * Constructor for the access pattern. This constructor is invoked by the framework. Must be public.
     */

   public SensorPattern(Resource device) {
      super(device);
   }

}

The generic argument Sensor of the ResourcePattern superclass is the OGEMA Resource type we are interested in. The reading field in this class is a subresource of a Sensor, containing the actual measurement data. Since all declared subresources in OGEMA are optional, it is possible in principle to create a Sensor resource that has no reading, i.e. no actual value. It could also happen that the current sensor value is not available due to communication problems, and the driver might have deactivated the reading subresource. Therefore, we cannot assume that every Sensor actually has a valid reading. In the SensorPattern class, however, we declare that we only want to be informed about Sensors whose reading subresource exists and is active.

In the next step, we adapt the listener class SensorListener in the package ending on patternlistener that also has been created when the pattern was added. The listener takes the following form:

import org.ogema.core.model.Resource;
import org.ogema.core.model.simple.IntegerResource;
import org.ogema.core.resourcemanager.pattern.PatternListener;
import org.ogema.model.sensors.Sensor;

/**
 * Keeps track of all {@link Sensor}s in the system.
 */

public class SensorListener implements PatternListener<SensorPattern> {

   private final IntegerResource numberSensors;

   public SensorListener(SensorAppController templateProcess) {
     this.numberSensors = templateProcess.nrSensors;
   }

   @Override
   public void patternAvailable(SensorPattern pattern) {
       // we know that the pattern model is of type Sensor, but it could be a derived
       // type, such as TemperatureSensor, PowerSensor, etc.
       Class<? extends Resource> sensorType = pattern.model.getResourceType();
       // the OGEMA resource path (database id) of the sensor
       String path = pattern.model.getPath();
       int nrSensors = numberSensors.getValue() + 1;
        System.out.println("New sensor available. Type: " + sensorType.getSimpleName() + ", path: " + path
           + ". Total number of sensors: " + nrSensors);
        numberSensors.setValue(nrSensors);
   }

   @Override
   public void patternUnavailable(SensorPattern pattern) {
        Class<? extends Resource> sensorType = pattern.model.getResourceType();
        String path = pattern.model.getPath();
       int nrSensors = numberSensors.getValue() -1;
        System.out.println("Sensor gone. Type: " + sensorType.getSimpleName() + ", path: " + path
             + ". Total number of sensors: " + nrSensors);
        numberSensors.setValue(nrSensors);
   }

}

It stores the number of sensors in an IntegerResource (one could replace this by an int in this example, the Java primitive integer type, since persistence is not relevant here), which must be passed to the constructor. Every time a new sensor matching our pattern declaration is available, the patternAvailable method will be called, and the listener prints the current number of sensors to the console, and similarly when a sensor disappears.

Finally we have to make sure we create the integer resource in the controller before the listener is created and registered by the controller. The code to create and register the listener is added automatically when a pattern is added. The constructor of the controller may look like this (in the tutorial app on Github some more lines have been added for demonstration, which are not really necessary for the functionality):

public SensorListener sensorListener;
public IntegerResource nrSensors;


public SensorAppController(ApplicationManager appMan) {
   this.appMan = appMan;
   this.log = appMan.getLogger();
   this.advAcc = appMan.getResourcePatternAccess();

    nrSensors = appMan.getResourceManagement().createResource("sensorCount", IntegerResource.class);
    nrSensors.activate(false);

    initDemands();
}

(add the missing imports, as in the Hello OGEMA app). Done. Now we can build the app (Run as -> Maven install), add it to the run configuration (stdRundir -> config -> config.xml), and start the framework. See Create a new app or Hello OGEMA for details.

Once the framework is running, we need to create a sensor or two to test the functionality. For this purpose, login to OGEMA in the browser (https://localhost:8443/ogema/index.html), and click on the Simulation GUI icon:

simgui.png

In the simulation GUI you can select between different simulation providers. Choose one of the basic simulations, such as Basic motion detector simulation, Basic sensor devices simulation or Basic door or window sensor simulation. They create new sensor resources with corresponding reading subresources containing completely random values. Every time we create a new simulated device, a log message should appear in the console, informing us about the new sensor count. Note that the Basic sensor devices simulation creates a sensor device that contains multiple sensors (including temperature, humidity, and others), so it increases the count by more than one. 

simgui2.png

Using the simulation GUI to create a new sensor device.


Add a GUI using the PatternPageUtil

The core functionality of the app is now available. The example in the tutorial repository contains two more functions, though. First, we can add a simple GUI to our app, that displays the available patterns. It will also cause our app to appear in its own tile on the OGEMA start screen. Simply add the following delcarations to the class SensorAppApp:

@Reference
OgemaGuiService widgetService;
private WidgetApp widgetApp;

and the following lines to the start method:

WidgetApp widgetApp = widgetService.createWidgetApp(urlPath, appManager);
PatternPageUtil ppu = PatternPageUtil.getInstance(appManager, widgetApp);
ppu.newPatternDisplayPage(SensorPattern.class, "index.html", true, null);

(add missing imports). Here we use a special tool that generates user pages from ResourcePattern declarations. It is possible to create more general user pages using the OGEMA widgets framework, but this is beyond the scope of this app (it will be introduced in the window-heat-control app tutorial, coming next). Rebuild the app, stop the framework and start it again (alternatively, update the app in the running framework as described here: Create new project#LifecycleManagementUsingTheOSGiShell). Open the OGEMA start page again, and look for the SensorApp. 

sensorAppGui.png

Sensor App GUI

Instantaneous pattern access

So far, we have registered a listener for the SensorPattern, and used the pattern declaration to create a simple GUI. It is also possible to query the OGEMA database for all current pattern matches, without registering a listener. This is illustrated by the following code, which can be added to the start method:

List<SensorPattern> sensors = appManager.getResourcePatternAccess().getPatterns(SensorPattern.class, AccessPriority.PRIO_LOWEST);
System.out.println("Initial sensor count is " + sensors.size());

This prints the number of pattern matches available at the start of the application. To check its functionality, let's build the app again, stop the running framework, and restart it, this time using the default_unclean.launch start configuration (which will not delete the resource database, contrary to the default_clean configuration). If we did create a couple of sensors before, we should now get a message that the initial sensor count is nonzero, as in the example output below:

sensorAppInitialCount.png

Get the Source Code

The source code of the sensor app is available from the Github repository, https://github.com/ogema/tutorial, folder src/sensor-app, or as a zip file.

Next


  • Now that we are familiar with the ResourcePatternAccess, the declaration of Resource demands by means of template classes, we can turn to a more realistic example app, which comes quite close to a production-ready app already. The window-heat-control app listens to multiple types of devices, and organizes them according to the room the devices are located in. Furthermore, we introduce you to more general user pages than the simplistic pattern-based one encountered here: WindowHeatControl App

Tags:
Created by Christoph Nölle on 2017/01/25 23:23