Abstract Driver Bundle

The Abstract Driver represents a utility API which provides a common implementation of a device driver and can be extended by different types of protocol drivers.

Contents:


Bundle Information

Bundle JAR

The JAR of the Abstract Driver Bundle is abstractdriver.jar, located in the bundles installation directory.

Import

Package Exporter Description
org.osgi.service.device OSGi Library Bundle Holds the main components, defined by the OSGi Device Access Specification. These components are supported and managed by the Device Manager Bundle.
org.mbs.services.cu.spi Control Unit Admin Bundle The Control Unit Provider API for implementation of control units.
org.mbs.services.cu.generator Control Unit Generator Support Bundle The Control Unit Generator API which extends the control unit paradigm by adding model-controller functionality.
com.prosyst.mbs.services.db DB Bundle Provides means for persistent storage of data in a database.
com.prosyst.util.ref

ProSyst Util Bundle

The log utility that stores log messages about the runtime status of a bundle.
com.prosyst.util.xml Classes for reading and processing XML documents.

Export

The Abstract Driver bundle exports the org.mbs.services.device.abstractdriver package which holds the Abstract Driver API.


Managed Service

The Abstract Driver Bundle registers an org.osgi.service.cm.ManagedService service in the OSGi framework for exporting properties configurable through the OSGi Configuration Admin Service. It provides the Log Debug Configuration of the Abstract Driver bundle whose PID is driver.debug. For further information on the configuration's properties, refer to the Configuration part of this document.


Abstract Driver API

Overview

The Abstract Driver API provides means for common implementation of a device driver. It holds the logic of attaching to Device services and registering device control units into the OSGi framework.

A device driver built on top of the Abstract Driver should perform the following functions:

Abstract Driver Functional Model

The figure below illustrates how the Abstract Driver functions in relations with other systems in the framework:


Figure 1: Abstract Driver functional model

API Structure

AbstractDeviceModel

This class can be used as a base class for all device models. It holds the current state of the device, represented by a number of state variables. It extends the org.mbs.services.cu.generator.DefaultControlUnitModel class and provides the handleInconsistency method which handles the cases of the device model inconsistency. This method is used only when there is a Device State Graph loaded for the model, otherwise is should stay empty.

AbstractDeviceContoller

The class AbstractDeviceController provides a common implementation of a Device controller. It provides methods mapped to control unit actions.

AbsrtactDeviceDriver

This class provides an implementation of the attach method of the org.osgi.service.device.Driver class. The extending classes have to implement the match method of the class to match the driver to the currently registered devices.

AbstractDeviceActivator

This class can be used to implement bundle activators for all device drivers. The only method it contains is the createDrivers method and all the subclasses should implement it.

Device State Graph

The Device State Graph is an XML file which is used to detect device model inconsistency. It defines a number of states in which the device can be while functioning. Each state is defined in the State Graph by:

A special condition determines the current state of the device. It depends of the values of certain device state variables.

The Device State Graph uses a special state variable to inform the user applications of the allowed actions of the device in any moment. This state variable has ID AbstractDeviceModel.SVAR_ALLOWED_ACTIONS that is equal to "svarAllowedAction". It contains a vector of string IDs of the allowed actions.

If an action which is not allowed in the current device state is invoked, then an exception is thrown to indicate that the invocation is not permitted.

The device model is marked inconsistent in any of the following cases:

In any case of model inconsistency, the extending device drivers are forced to take care of this situation. This usually means resynchronizing the device model with the physical device.

The Device State Graph XML must follow the syntax[stategraph.dtd]:

DTD Elements Description
<!ELEMENT state-graph (state+)>
The state-graph element provides a full state graph description, which consists of a number of device states.
<!ELEMENT state (condition, target-states, possible-svar-events, allowed-actions)>
Each state element contains a state condition, a list of target states, a list of possible state variable events and a list of allowed action invocations.
<!ATTLIST state-id CDATA #REQUIRED>
The state element has one mandatory attribute and it provides the unique ID of the state.
<!ELEMENT condition (#PCDATA)>
The condition element contains logical expression, including state variable IDs and constant values. When the condition is satisfied, i.e. the expression is true, then it is sure that this state is the active (current) state of the device.
<!ELEMENT target-states (state-id*)>
The target-states element contains a list of states, to which transition is allowed from this state.
<!ELEMENT possible-svar-events (svar-id*)>
The possible-svar-events element contains a list of state variable IDs, which are the only ones that can trigger events when device is in this state.
<!ELEMENT allowed-actions (action-id*)>
The allowed-actions element contains a list of action IDs, which are the only ones to be invoked when the device is in this state.


Creating a Driver on Top of the Abstract Driver

Create the Device Control Unit XML

The control unit XML of a device which uses the Abstract Driver, must have an additional attribute definition in order to inherit the attributes of the Abstract Device control unit. This attribute must have ID "super", type string and default value "abstractDevice". The other part of the control unit XML follows the control unit metatype.

Tip: To learn details on the way a control unit metadata is defined, refer to the Control Unit Admin Bundle document.

The application below is used as an example Device service in the current document. It represents a part of a hi-fi system and implements the org.osgi.service.device.Device interface. It has three state variables - one to represent the volume, one to enable the radio of the system and one representing the radio frequency. The device, also, provides a number of properties which will uniquely identify it in the framework:

package my.device;

import java.util.Hashtable;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.device.Device;
import org.osgi.service.device.Constants;

public class HifiSystemDevice implements Device, BundleActivator {
	
   ServiceRegistration servReg = null;
	
   private int vol = -1;
   private int freq = -1;
   private boolean radioEnabled = false;
	
   public HifiSystemDevice() {
      vol = 0;
	   freq = 0;
   }

   public void noDriverFound() {
      vol = -1;
      freq = -1;
      radioEnabled = false;
   }

   public void start(BundleContext bc) throws Exception {
      Hashtable hifiProps = new Hashtable();
      hifiProps.put(Constants.DEVICE_CATEGORY, "hifiSystem");
      hifiProps.put(Constants.DEVICE_SERIAL, "001");
      hifiProps.put(Constants.DRIVER_ID, "hifiDriver");
		
      servReg = bc.registerService(Device.class.getName(), this, hifiProps);
   }

   public void stop(BundleContext bc) throws Exception {	
      servReg = null;
   }
}
Listing 1: Register a Device service, representing a hi-fi system

The listing below is the control unit XML of the hi-fi system device from Listing 1:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE metatype-provider SYSTEM "metatype.dtd">
<metatype-provider>
   <objectclass><locale>en</locale>
      <name>Test Hifi System Control Unit</name>
      <id>hifiSystem</id>
      <description>A simple control unit represnting hifi system</description>

      <attribute modifier="req">
          <name/>
          <id>super</id>
          <description/>
          <type>&string;</type>	
          <value>
             <scalar>abstractDevice</scalar>
          </value>
      </attribute>

<!--state variables-->
     <attribute modifier="req">
         <name>Volume</name>
         <id>volume</id>
         <description>Current volume of the hifi system</description>
         <type>&int;</type>
         <key name="minValue" value="0"/>
         <key name="maxValue" value="20"/>
         <cardinality>0</cardinality>
        </attribute>
      <attribute modifier="req">
         <name>Radio Enabled</name>
         <id>radioEnabled</id>
         <description>Enbles the radio of the hifi system</description>
         <type>&boolean;</type>
         <cardinality>0</cardinality>
         <value>
            <scalar>false</scalar>
         </value>
     </attribute>
     <attribute modifier="req">
        <name>Radio Frequency</name>
        <id>radioFreq</id>
        <description>Current radio frequency</description>
        <type>&float;</type>
        <key name="minValue" value="60"/>
        <key name="maxValue" value="180"/>
        <cardinality>0</cardinality>
     </attribute>

<!--actions-->
     <objectclass>
        <locale/>
        <name>Set Volume</name>
        <id>setVol</id>
        <description>Changes the volume of the hifi system.</description>
        <attribute modifier="in">
           <name>Volume</name>
           <id>volume</id>
           <description>New hifi system volume</description>
           <type>&int;</type>
           <key name="minValue" value="0"/>
           <key name="maxValue" value="20"/>
           <cardinality>0</cardinality>
        </attribute>
     </objectclass>
     <objectclass>
        <locale/>
        <name>Enable Radio</name>
        <id>enableRadio</id>
        <description>Enables the radio of the hifi system.</description>
        <attribute modifier="in">
           <name>Radio Enabled</name>
           <id>radioEnabled</id>
           <description>Shows wether the radio is enabled or disabled</description>
           <type>&boolean;</type>
           <cardinality>0</cardinality>
           <value>
              <scalar>false</scalar>
            </value>
        </attribute>
      </objectclass>
      <objectclass>
         <locale/>
         <name>Set Radio Frequency</name>
         <id>setRadioFreq</id>
         <description>Changes the value of the Radio Frequency variable.</description>
         <attribute modifier="in">
            <name>Radio Frequency</name>
            <id>radioFreq</id>
            <description>New radio frequency</description>
            <type>&float;</type>
            <key name="minValue" value="60"/>
            <key name="maxValue" value="180"/>
            <cardinality>0</cardinality>
        </attribute>
      </objectclass>
    </objectclass>
</metatype-provider>
Figure 2: Hi-fi system device control unit XML

Create the device model and the device controller

To create the classes of the device model and device controller, use the Control Unit Generator tool.

Before giving the Control Unit generator tool the proper commands to generate the classes, you have to set the MODELSUPERCLASS property to org.mbs.services.device.abstractdriver.AbstractDeviceModel and the CONTOLLERSUPERCLASS property to org.mbs.services.device.abstractdriver.AbstractDeviceController.

Tip: To learn how to use the Control Unit Generator tool, refer to the Control Unit Generator Tool document.

The generated class of the device model extends the AbstractDeviceModel class and does not require any additional development. If there is a Device State Graph loaded for the device, you should implement the handleInconsistency method. For example, in the handleInconsistency method implementation you can call the reset method, inherited from the org.mbs.services.cu.generator.DefaultControlUnitModel class, to reset the device model.

package my.driver;

import java.util.Hashtable;
import org.mbs.services.device.abstractdriver.AbstractDeviceModel;

public class HifiSystemControlUnitModel extends AbstractDeviceModel {

   // Automatically generated source code
   public static final String SVAR_VOLUME = "volume";
   public static final String SVAR_RADIO_ENABLED = "radioEnabled";
   public static final String SVAR_RADIO_FREQ = "radioFreq";

   // Automatically generated source code
   public HifiSystemControlUnitModel(Object arg1, String arg2, String arg3) {
      super(arg1, arg2, arg3);
   }

   // Automatically generated source code
   protected Hashtable getModelInitValues(Hashtable table) {
      table = super.getModelInitValues(table);
      table.put(SVAR_VOLUME, INTEGER_ZERO);
      table.put(SVAR_RADIO_ENABLED, Boolean.FALSE);
      table.put(SVAR_RADIO_FREQ, new Float((float) 60));
      return table;
   }

   protected void handleInconsistency() {
      // TODO Auto-generated method stub
       try {
          reset();
       } catch (Exception e) {
          e.printStackTrace();
       }
   }
}
Listing 3: Hi-fi system device model class

The generated class of the device controller extends the AbstractDeviceController class. Unlike the class of the device model, it needs further development to become functional.

The controller contains Java methods for mapping control unit actions in compliance with the requirements of the Control Unit Generator API . Method names start with the _cu_ prefix so that the default control unit implementation can locate and invoke actions correctly on user request. In these methods you have to call the updateSV method of the device model class in order to change the values of the corresponding state variables. It checks if the old value of the state variable is different from its new one and notifies the interested listeners. This method requires the control unit ID and the new value of the state variable.

The listing below contains the class of the device controller for the hi-fi system device:

package my.driver;
import org.mbs.services.device.abstractdriver.AbstractDeviceController;
import my.device.HifiSystemDevice;

public class HifiSystemControlUnitController extends AbstractDeviceController {

   HifiSystemDevice device = null;
   
   // Automatically generated source code
   public static final String ACTION_SET_VOL = "setVol";
   public static final String ACTION_ENABLE_RADIO = "enableRadio";
   public static final String ACTION_SET_RADIO_FREQ = "setRadioFreq";

   // Automatically generated source code
   public HifiSystemControlUnitController(HifiSystemDevice device) {
      super(device);
      this.device = device;
   }

   // Automatically generated source code
   public void _cu_setVol(int volume) {
      model.updateSV(HifiSystemControlUnitModel.SVAR_VOLUME, new Integer(volume));
   }

   // Automatically generated source code
   public void _cu_enableRadio(boolean enableRadio) {
      model.updateSV(HifiSystemControlUnitModel.SVAR_RADIO_ENABLED, new Boolean(enableRadio));
   }

   // Automatically generated source code
   public void _cu_setRadioFreq(float radioFreq) {
      model.updateSV(HifiSystemControlUnitModel.SVAR_RADIO_FREQ, new Float(radioFreq));
   }
}
Listing 4: Hi-fi system device controller class

Create the Abstract Driver

To create the Abstract Driver, extend the AbstractDeviceDriver class. The methods that should be implemented are the following:

The listed application is the implementation of the Abstract Driver for the hi-fi system device.

package my.driver;

import org.mbs.services.device.abstractdriver.AbstractDeviceController;
import org.mbs.services.device.abstractdriver.AbstractDeviceDriver;
import org.mbs.services.device.abstractdriver.AbstractDeviceModel;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.device.Constants;
import org.osgi.service.device.Device;

import my.device.HifiSystemDevice;

public class TestAbstractDriver extends AbstractDeviceDriver{

   protected final static String HIFI_DRIVER_ID  = "hifiDriverID";
   protected final static String HIFI_CU_ID = "hifiSystem";
   String cu_ID = "001";
   static final String SERVICE_FILTER = "(objectClass="	+
                          HifiSystemDevice.class.getName() + ")";
	
   public TestAbstractDriver(BundleContext bc) throws Exception {
      super(bc);
   }

   public int match(ServiceReference ref) throws Exception {
      if((ref.getProperty(Constants.DEVICE_CATEGORY)).equals(HIFI_CU_ID)){
         HifiSystemDevice device = (HifiSystemDevice) bc.getService(ref);
         cu_ID = (String) ref.getProperty(Constants.DEVICE_SERIAL);
         return Integer.MAX_VALUE;
      }
      return Device.MATCH_NONE;
   }

   public String getDriverID() {
      return HIFI_CU_ID;
   }

   protected AbstractDeviceController getController(Object device) {
      HifiSystemControlUnitController controller = null;
      if(device instanceof HifiSystemDevice) {
         HifiSystemDevice new_device = (HifiSystemDevice) device;
         controller = new HifiSystemControlUnitController(new_device);
      }
      return controller;
   }

   protected AbstractDeviceModel getModel(Object device) {
      HifiSystemControlUnitModel model = null;
      if(device instanceof HifiSystemDevice) {
         HifiSystemDevice new_device = (HifiSystemDevice) device;
         model = new HifiSystemControlUnitModel(new_device, HIFI_CU_ID, cu_ID);
      }
      return model;
   }

   public String generateControlUnitID(Object device) {
      return HIFI_CU_ID;
   }

   public String getDeviceServiceFilter() {
      return SERVICE_FILTER;
   }
}
Listing 5: Create the Abstract Driver

Create the Abstract Driver Activator

To create the Abstract Driver Activator, extend the org.mbs.services.device.abstractdriver.AbstractDeviceActivator class. Implement the createDrivers method to return an instance of the driver:

package my.driver;

import org.mbs.services.device.abstractdriver.AbstractDeviceActivator;
import org.mbs.services.device.abstractdriver.AbstractDeviceDriver;


public class TestAbstractDriverActivator extends AbstractDeviceActivator{

   public AbstractDeviceDriver[] createDrivers() throws Exception {	
      AbstractDeviceDriver[] drivers = new AbstractDeviceDriver[1];
      drivers[0] = new TestAbstractDriver(bc);	
      return drivers;
   }

}
Listing 6: The Abstract Driver activator

Create the Device State Graph XML

The following State Graph XML is defined to handle the inconsistency of the hi-fi system device model. It defines two possible states: when the "radioEnabled" state variable is "false" and when it is "true". This information is contained in the "condition" element of the corresponding state. In the first state the possible state variable events exclude the "radioFreq" state variable as the radio of the system is not enabled. Respectively, the allowed actions in this state do not include the one which changes the radio frequency. In opposite, in the second state the allowed state variable events include the "radioFreq" variable and its corresponding action is included in the "allowed-actions" element as well.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE state-graph SYSTEM "stategraph.dtd">
<state-graph>

   <state id="1">
      <condition>(radioEnabled=false)</condition>
      <target-states>
         <state-id>2</state-id>
      </target-states>
      <possible-svar-events>
         <svar-id>volume</svar-id>
         <svar-id>radioEnabled</svar-id>
      </possible-svar-events>
      <allowed-actions>
         <action-id>setVol</action-id>
         <action-id>enableRadio</action-id>
      </allowed-actions>
   </state>

   <state id="2">
      <condition>(radioEnabled=true)</condition>
      <target-states>
         <state-id>1</state-id>
      </target-states>
      <possible-svar-events>
         <svar-id>radioFreq</svar-id>
         <svar-id>volume</svar-id>
         <svar-id>radioEnabled</svar-id>
      </possible-svar-events>
      <allowed-actions>
         <action-id>setRadioFreq</action-id>
         <action-id>setVol</action-id>
         <action-id>enableRadio</action-id>
      </allowed-actions>
   </state>

</state-graph>
Listing 7: The Device State Graph XML

Write the Manifest File

In the Import-Package header of the manifest you have to include the org.mbs.services.cu.generator and org.mbs.services.cu packages in order to resolve the dependencies of your driver.

To register the metadata provided by the control unit XML and the Device State Graph, you have to include the following manifest headers:

After packing the Abstract Driver in a JAR, it is ready to be deployed into the framework.


Configuration

Configuration Resources

The Managed Service service, provides the Log Debug Configuration whose PID is driver.debug. It allows you to configure the log debug properties for the Abstract Driver:

Property ID Name Type Default Value Description
DEBUG_ALL DEBUG_ALL boolean false Logs all debug information.
PRINT_ON_CONSOLE PRINT_ON_CONSOLE boolean false Prints all debug messages in the system output.
MODEL LOG_MODEL boolean false Logs the debug information for all the device models.
CONTROLLER LOG_CONTROLLER boolean false Logs the debug information for all the device controllers.
DRIVER LOG_DRIVER boolean false Logs the debug information for the Abstract Driver.

Visual Administration

You can easily manage the properties of the Log Debug Configuration of the Abstract Driver bundle through an mConsole general property editor.

To enter the property editor:

  1. In the connected mConsole, enter the Bundles tab.
  2. Unfold the Device node of the bundles tree.
  3. Unfold the Abstract Driver Bundle node. You will see that it contains a configuration named "Abstract Driver Log Configuration".
  4. Select the Abstract Driver Log Configuration node. The configuration properties will appear in the right pane.


Figure 1: Log Debug Configuration of the Abstract Driver bundle

System Properties

System Property Default Value Description
mbs.abstractdriver.token false It defines if the default class of the control unit created by the device driver on attaching to a device will support the token locking mechanism. In brief, this mechanism resolves possible conflicts in the case of concurrent access to a control unit instance. See the "Control Unit Generator Support Bundle" document.


References


Device Bundles