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:
The JAR of the Abstract Driver Bundle is abstractdriver.jar, located in the bundles installation directory.
|
The Abstract Driver bundle exports the org.mbs.services.device.abstractdriver
package which holds the Abstract Driver API.
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.
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:
The figure below illustrates how the Abstract Driver functions in relations with other systems in the framework:

Figure 1: Abstract Driver functional model
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.
The class AbstractDeviceController provides a common implementation
of a Device controller. It provides methods mapped to control unit actions.
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.
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.
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]:
|
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;
}
}
|
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>∫</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>∫</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> |
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();
}
}
}
|
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));
}
}
|
To create the Abstract Driver, extend the AbstractDeviceDriver
class. The methods that should be implemented are the following:
match - Checks if the driver is suitable for the Device service.
The Device service is represented by a given Service Reference and returns
a value which indicates the level of support the driver can provide. If the
driver cannot support the Device service at all, Device.MATCH_NONE
is returned. You can perform the checkup by querying the properties provided
by the Device service or by getting the referenced Device service object,
as long as you unget the service and return the physical device to a normal
state before this method returns.getDriverID - Returns the driver ID of the implementing classes.
In the return statement you have to pass the ID of the device
control unit as defined in the control unit XML.getContoller - Returns the AstractDeviceController
instance for each device.getModel - Returns the AstractDeviceModel instance
for each device. generateControlUnitID - Generates the ID of the control unit.
It requires an instance of the Device service passed as parameter. In the
return statement of the method you have to pass the control unit
ID.getDeviceServiceFilter - Registers a driver service listener
with service filter. In the return statement of the method you
have to pass a filter for Device services in compliance with the OSGi Framework
Specification.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;
}
}
|
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;
}
}
|
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> |
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.
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:
|
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:

Figure 1: Log Debug Configuration of the Abstract Driver
bundle
|