This article continues our series on how to build adapters and agents to integrate RFID readers into the IBM® Websphere® RFID solution. Part 1 described the overall solution and the WebSphere RFID Device Infrastructure. Part 2 showed you how to implement the transport layer. In this article, you'll learn how to implement the device layer. Once again, we'll use the Sirit INfinity 510 reader as an example.
If you'll recall from Part 1, we use the Device Kit component of WebSphere RFID Device Infrastructure to implement an adapter for a reader. Device Kit consists of several layers, as shown in Figure 1. In Part 2 we built a transport. In this article, we'll continue implementing the adapter by constructing a device.
Figure 1. Device Kit adapter
A device provides a container for a set of objects called controls that model a hardware device's commands and messages. There are three types of controls: commands, signals and measurements. Commands model a hardware device's commands. Signals are used to model the messages received from the hardware device. Messages are typically received as the result of executing commands, but may also be asynchronous. Commands and signals together model the messages that can be sent to and received from a device. Measurements are used to model a property or attribute of the hardware device.
Devices are concerned only with modeling the controls of a hardware device. All communication with the hardware device is handled by the underlying transport.
Devices are implemented in Java™ code generated by the Device Kit tools. This code is generated from the device's Control Markup Language (CML) file. Every device has a CML file, which contains definitions that describe the device and the controls that model the hardware device's commands and messages. Agents in the WebSphere RFID solution use the generated Java code to send commands to and receive responses from the hardware device. For example, an agent would use a device's controls to instruct the associated RFID reader to start and stop reading and to receive read reports for singulated RFID tags.
When building a device, all of the development work is done in CML. You don't have to develop any Java code. All of the Java code is generated by the Device Kit tools from the CML.
The Device Kit Device Creation wizard creates the initial CML file and necessary device projects. The generated CML file includes the CML device definition and, if specified at creation time, a definition for the transport. Listing 1 shows the generated CML file for a newly created device called SimpleDevice.
Listing 1. Generated CML file
<?xml version="1.0"?>
<!--Licensed Materials - Property of My Company-->
<!--(C) Copyright My Company Corp. 2006 All Rights Reserved-->
<!--US Government Users Restricted Rights - Use, duplication or disclosure-->
<!--restricted by GSA ADP Schedule Contract with My Company Corp.-->
<cml packagebase="com.mycompany" format="hex">
<version>3.3.0</version>
<vendor>My Company</vendor>
<device id="SimpleDevice"
bundle="SimpleDevice"
implementation="SimpleDevice">
<description>Simple Device</description>
<transportservice service=
"com.mycompany.simple.transport.service.SimpleTransportService"
implementation="com.mycompany.simple.transport.SimpleTransport"/>
<bundle/>
</device>
</cml>
|
The <device> element defines a device. Commonly used <device> element attributes are:
id- specifies the unique identifier for the<device>element.bundle- specifies the Open Services Gateway Initiative (OSGi) bundle name. This value is used to set the value of the Bundle-Name header in the bundle Manifest file.implementation- specifies the name of the generated device implementation class.
The <device> element can include other elements. Commonly included elements are:
<description>- specifies a description of the device.<command>- specifies a command definition.<signal>- specifies a signal definition.<measurement>- specifies a measurement definition.<transportservice>- specifies the transport service used by the device.
For a complete list of the CML elements and attributes that can be used in a device definition, refer to the WebSphere Studio Device Developer Device Kit help.
Every device has a core class that contains the device's controls, a message class that contains the messages used by the controls, and a service interface that contains a set of keys for accessing the controls.
Device objects implement the com.ibm.esc.device.service.DeviceService interface. Each control (command, measurement, and signal) defined in the device CML file is accessible by a key, defined in the service interface class (see below). The DeviceService interface defines the following methods for accessing a device's controls:
getCommand(String key)- returns theCommandServiceobject identified by the key.getSignal(String key)- returns theSignalServiceobject identified by the key.getMeasurement(String key) - returns theMeasurementServiceobject identified by the key.
The DeviceService interface defines two additional methods that allow objects implementing the com.ibm.esc.device.service.DeviceListener interface to add and remove themselves as device listeners. The methods for adding and removing a device listener are: addDeviceListener(DeviceListener)and removeDeviceListener(DeviceListener). Device listeners are notified whenever one of the following device events occur:
- State change. A
DeviceServiceobject can be in one of the following states: DEAD, CREATED, ALIVE, CONNECTED, STARTED. ADeviceServiceobject must be in the STARTED state before it can be used to execute commands on the hardware device. - When a control is added to or removed from a
DeviceServiceobject or when an existing control is modified.
The message class contains field definitions for the messages, parameters and filters defined in the device CML file along with getter methods for the fields. For example, suppose the device CML file contains a definition that models a hardware device command. The command definition would include a message definition that specifies the message (string, bytes, and so on) that would be sent to the hardware device to execute the command. The message might also include a parameter definition indicating the command requires a parameter. In this case, the message class would include field definitions and getter methods for both the message and parameter.
See Part 1 for a discussion of messages.
The service interface extends the DeviceService interface and contains a set of static field definitions. There is one field definition for each control defined in the CML file. Each field is of type String. The value of the control's CML id attribute is used as the field name and value. The control ID serves as the key for getting and putting controls in a DeviceService object.
Objects that add themselves as device listeners must implement the DeviceListener interface. This interface defines the methods that are called when a device event occurs. The methods defined by the DeviceListener interface are:
controlChanged(DeviceService device, Object timestamp, ControlService control, int code)- Thecontrolparameter contains the control that was changed. Thecodeparameter specifies whether the control was added, removed, or modified.deviceChanged(DeviceService source, Object timestamp, int newState, int oldState)- ThenewStateparameter contains the new state of the device (DEAD, CREATED, and so on). TheoldStateparameter contains the old state of the device.
Let's look at a simple example to illustrate how the various device pieces fit together. In this example, we'll add a command definition to the SimpleDevice.cml file shown earlier. The command definition will model the Sirit INfinity 510 version.sw command. To execute this command, we need to send the string version.sw\r\n to the hardware device. The CML required to model this command is shown in Listing 2.
Listing 2. CML to model the version.sw command
<command id="VersionSwGetRequest">
<message id="VersionSwGetRequestMessage">
<ascii>version.sw\r\n</ascii>
</message>
</command>
|
After adding the above command definition to the SimpleDevice.cml file and re-generating the device code, the field definition is added to the SimpleDevice class:
private final MessageCommand VersionSwGetRequest =
new MessageCommand(
SimpleDeviceService.VersionSwGetRequest,
SimpleDeviceMessages.getVersionSwGetRequestMessage());
|
The MessageCommand(String key, MessageService message) constructor requires two parameters. The first one is the command's key and the second is the command's message. The key is obtained from the static field SimpleDeviceService.VersionSwGetRequest. This field is initialized with the string "VersionSwGetRequest". The field's value comes from the id attribute specified on the <command> CML element. The message is obtained from the SimpleDeviceMessage class. This class contains a field definition initialized with an object implementing the MessageService interface and a getter method for the object. The message is an AsciiMessage object whose value is a byte array constructed from the value of the <ascii> CML element specified in the command message definition:
private static final MessageService VersionSwGetRequestMessage =
new AsciiMessage(
new byte[] {'v','e','r','s','i','o','n','.','s','w','\r','\n' });
|
The application code required to obtain the VersionSwGetRequest command is shown in Listing 3.
Listing 3. Application code to obtain a control
// define the variable to hold the controls
CommandService versionSwGetRequestCommand;
// create the device
DeviceService device = new SampleDevice();
// get the control
versionSwGetRequestCommand =
device.getCommand(SimpleDeviceService.VersionSwGetRequest);
|
As you can see, the code to get a control is very simple. All you need is the DeviceService object and the control's key.
A command represents a request that can be sent to a hardware device to perform a specific operation or function. Commands are defined in CML. The Device Kit tools generate the Java code implementing the command from the CML definitions.
Each command definition includes a message definition that specifies the bytes that get sent to the hardware device to execute the command. The message definition must include all required parameters, command terminators, and so on. The format and content of the message is determined by the hardware device specification. For example, the hardware specification specifies whether the protocol is ASCII- or binary-based, any character sequence used to terminate commands and parameter separators. While the format of a message differs from hardware device to hardware device, the approach used to model them in CML is consistent.
A <command> element defines a command. The id attribute specifies the unique identifier for the <command> element.
The <command> element can include other elements. Commonly included elements are:
<specreference>- specifies a reference to the hardware specification documentation.<description> -specifies a description of the command.<message>- specifies the message (string, bytes, and so on) sent to the hardware device to execute the command.
For a complete list of the CML elements and attributes that can be used in a command definition, refer to the WebSphere Studio Device Developer Device Kit help.
Command control objects are instances of classes that implement the com.ibm.esc.command.service.CommandService interface. This interface defines methods for executing a command. It also defines methods that allow objects implementing the com.ibm.esc.command.service.CommandListener interface to add and remove themselves as command listeners. The methods defined by the CommandService interface are:
addCommandListener(CommandListener) - adds a listener to receive command listener notifications.removeCommandListener(CommandListener)- removes the listener.execute()- executes the command.execute(Object)- executes the command with a parameter.
Objects that add themselves as command listeners must implement the CommandListener interface. This interface defines a single method that is called when the command is executed. It's important to note that the method is called when the command is executed, not when the response from executing the command is received. The method defined by the CommandListener interface is:
commandExecuted(CommandService source, Object timestamp, Object data) |
Let's look at an example to see how commands work. In this example, we'll add a new command definition that requires a parameter to the SimpleDevice.cml file. We'll add a command definition that models the Sirit INfinity 510 setup.operating_mode command.
This command sets the operational mode of the reader to one of the following: autonomous, polled, or standby.
The format of the command is: setup.operating_mode = mode\r\n. The value mode is the command parameter and \r\n is the command terminator. The CML required to model this command is shown in Listing 4.
Listing 4. CML to model a command
<command id="SetupOperatingModeSetRequest">
<message id="SetupOperatingModeSetRequestMessage">
<ascii>setup.operating_mode = \r\n</ascii>
<parameter type="string">
<insert/>
<index>23</index>
</parameter>
</message>
</command>
|
The command message is defined as a template message (see Part 1 for a discussion of template and concrete messages), allowing a parameter to be specified when the command is executed. By specifying a template message, we can use the same command to set the operational mode of the reader to any one of the supported modes.
After adding the command definition to the SimpleDevice.cml file and re-generating the device code, the field definition shown in Listing 5 is added to the SimpleDevice class. A new static field called SetupOperatingModeSetRequest is also added to the SimpleDeviceService interface. This field contains the control ID.
Listing 5. Code generated from a CML command definition
private final DataCommand SetupOperatingModeSetRequest =
new DataCommand(
SimpleDeviceService.SetupOperatingModeSetRequest,
SimpleDeviceMessages.getSetupOperatingModeSetRequestMessage());
|
The application code required to execute the SetupOperatingModeSetRequest command and set the reader's operational mode to autonomous is shown in Listing 6.
Listing 6. Application code to execute a command
// define the variable to hold the control
CommandService setupOperatingModeSetRequestCommand;
// get the control
setupOperatingModeSetRequestCommand =
device.getCommand(SimpleDeviceService.SetupOperatingModeSetRequest);
// execute the command
setupOperatingModeSetRequestCommand.execute("autonomous");
|
A signal is an asynchronous notification that data has been received from a hardware device. This data typically results from executing a command, but may also be an event notification. Signals are defined using CML. The Device Kit tools generate the Java code to implement the signal from the CML definitions.
Each signal definition includes a message definition that specifies the bytes sent from the hardware device. The message definition must include all required parameters, filter definitions, response terminators, and so on. The format and content of the message is determined by the hardware device specification. For example, the hardware specification specifies whether the protocol is ASCII- or binary-based, any character sequence used to terminate responses, and parameter separators.
A <signal> element defines a signal. The id attribute specifies the unique identifier for the <signal> element.
The <signal> element can include other elements. Commonly included elements are:
<specreference>- specifies a reference to the hardware specification documentation.<description>- specifies a description of the signal.<message>- specifies the message (string, bytes, and so on) sent from the hardware device.
For a complete list of the CML elements and attributes that can be used in a signal definition, refer to the WebSphere Studio Device Developer Device Kit help.
Signal control objects are instances of classes that implement the com.ibm.esc.signal.service.SignalService interface. This interface defines two methods. These methods allow objects implementing the com.ibm.esc.signal.service.SignalListener interface to add and remove themselves as signal listeners. The methods defined by the SignalService interface are:
addSignalListener(SignalListener)- adds a listener to receive signal listener notifications.removeSignalListener(SignalListener)- removes the listener.
Objects that add themselves as signal listeners must implement the SignalListener interface. This interface defines a single method that is called when a message is received from the hardware that matches the message defined in the signal. The method defined by the SignalListener interface is:
signalOccurred(SignalService source, Object timestamp, Object data) |
The source parameter identifies the signal the notification is for and the data parameter contains any parameters contained in the messages sent by the hardware device. If for example, you modeled a response for a command that gets a hardware device property value, the data parameter would include the property value returned by the hardware device.
Let's look at an example to see how signals work. In this example, we'll add a new signal definition to the SimpleDevice.cml file that models the response to the Sirit INfinity 510 version.sw command. The response from the reader to this command is ok 0.2.171\r\n\r\n. ok is the command response , 0.2.171 is the software version and \r\n\r\n is the response terminator. The CML required to model this signal is shown in Listing 7.
Listing 7. CML to model a signal
<signal id="VersionSwReport">
<message id="VersionSwReportMessage">
<ascii>ok \r\n\r\n</ascii>
<parameter type="string">
<insert/>
<index>3</index>
</parameter>
<filter>
<bytes format="hex">ff,ff,ff</bytes>
</filter>
<sentmessage idref="VersionSwGetRequestMessage"/>
</message>
</signal>
|
The message is defined as a template message. The <parameter> element ensures the software version is extracted from the incoming message and made available to the signal listeners. The <filter> element ensures the signal is triggered and the listeners notified any time a response is received from executing the version.sw command, not just when a certain response includes a certain version. The <sentmessage> element ensures that signal is triggered only when the incoming message is a response to a version.sw command.
After adding the signal definition to the SimpleDevice.cml file and regenerating the device code, the field definition shown in Listing 8 is added to the SimpleDevice class. A new static field called VersionSwReport is also added to the SimpleDeviceService interface. This field contains the control ID.
Listing 8. Code generated by a CML signal definition
private final DataSignal VersionSwReport =
new DataSignal(
SimpleDeviceService.VersionSwReport,
SimpleDeviceMessages.getVersionSwReportMessage());
|
The application code required to receive notifications when the VersionSwReport signal is triggered is shown in Listing 9.
Listing 9. Application code to use a signal
public class SimpleDeviceTestExample implements SignalListener {
// define the variable to hold the control
SignalService versionSwReportSignal;
public void aMethod() {
// get the control
versionSwReportSignal =
device.getSignal(SimpleDeviceService.VersionSwReport);
// add the listener
versionSwReportSignal.addSignalListener(this);
}
// process the signal
public void signalOccurred(
SignalService source,
Object timestamp,
Object data) {
// Code to process signal data goes here
}
}
|
Measurements are implemented using commands and signals. They combine the functions of these two controls into a single control to provide applications with a convenient mechanism for getting and setting property values on a hardware device. Measurements maintain a local cached copy of the property value. This feature provides convenient access to hardware device properties whose values are unlikely to change. Measurements are defined using CML. The Device Kit tools generate the Java code implementing the measurement from the CML definitions.
Measurement definitions typically define or reference a read command, a write command and a signal. The read command allows the measurement to be used to send a command to the hardware device to get the property value. The write command allows the measurement to be used to update the property on the hardware device. The signal is used to trigger updates to the local cached copy of the property value. It's important to note that the local copy of the property will only be updated when a message is received that causes the signal to be triggered. Omitting a definition or reference to any of these controls will limit the functionality of the measurement. If for example, you omit a definition or reference to a write command, you won't be able to use the measurement to update the property on the hardware device. If you omit the definition or reference to a signal, the measurement's local cached copy of the property won't be updated.
A <meaurement> element defines a measurement. The id attribute specifies the unique identifier for the <measurement> element.
The <measurement> element can include other elements. Commonly included elements are:
<specreference>- specifies a reference to the hardware specification documentation.<description>- specifies a description of the measurement.<readcommand>- defines or references the command used to read the property value on the hardware device.<writecommand>- defines or references the command used to update the property value on the hardware device.<signal>- defines of references the signal response from the hardware device that contains the measurement value.
For a complete list of the CML elements and attributes that can be used in a measurement definition, refer to the WebSphere Studio Device Developer Device Kit help.
Measurement control objects are instances of classes that implement the com.ibm.esc.measurement.service.MeasurementService interface. This interface defines methods for getting and setting a measurement. It also defines methods that allow objects implementing the com.ibm.esc.measurement.service.MeasurementListener interface to add and remove themselves as measurement listeners. The methods defined by the MeasurementService interface are:
addCommandListener(MeasurementListener)- adds a listener to receive measurement listener notifications.removeCommandListener(MeasurementListener)- removes the listener.executeRead()- executes the read command to perform an asynchronous read of the property on the hardware device. To get the value of the property, an application would also have to execute thegetValue()method or add itself as a listener of the signal defined or referenced by the<readcommand>.executeWrite(Object)- executes the write command with a parameter to update the property on the hardware device.getValue()- gets the measurement's local cached copy of the property.read(long)- executes a synchronous read of the property on the hardware device.
Objects that add themselves as measurement listeners must implement the MeasurementListener interface. This interface defines a single method that will be called whenever the value of the local cached copy of the property changes. The method defined by the MeasurementListener interface is:
measurementChanged(MeasurementService source, Object timestamp,
Object newValue, Object oldValue)
|
The source parameter identifies the measurement the notification is for. The newValue parameter contains the updated property value and the oldValue parameter contains the old property value.
Let's look at example to get a better understanding of how measurements work. In this example, we'll add a new measurement definition to the SimpleDevice.cml file that models the Sirit INfinity 510 setup.operating_mode variable. We've already gone over a command definition to update this property. Now we'll use that command definition and create a new one to read the property value on the reader. We'll also create a signal definition that will allow the measurement's local cached copy of the property to be updated. The CML required to model the measurement is shown in Listing 10.
Listing 10. CML for a measurement
<!-- Define the read command -->
<command id="SetupOperatingModeGetRequest">
<message id="SetupOperatingModeGetRequestMessage">
<ascii>setup.operating_mode\r\n</ascii>
</message>
</command>
<!-- Define the write command -->
<command id="SetupOperatingModeSetRequest">
<message id="SetupOperatingModeSetRequestMessage">
<ascii>setup.operating_mode = \r\n</ascii>
<parameter type="string">
<insert/>
<index>23</index>
</parameter>
</message>
</command>
<!-- Define the signal -->
<signal id="SetupOperatingModeReport">
<message id="SetupOperatingModeReportMessage">
<ascii>ok \r\n\r\n</ascii>
<parameter type="string">
<insert/>
<index>3</index>
</parameter>
<filter>
<bytes format="hex">ff,ff,ff</bytes>
</filter>
<sentmessage idref="SetupOperatingModeGetRequestMessage"/>
</message>
</signal>
<!-- Define the measurement -->
<measurement id="SetupOperatingMode">
<readcommand idref="SetupOperatingModeGetRequest"/>
<writecommand idref="SetupOperatingModeSetRequest"/>
<signal idref="SetupOperatingModeReport"/>
</measurement>
|
After adding the measurement definition to the SimpleDevice.cml file and regenerating the device code, the field definition shown in Listing 11 is added to the SimpleDevice class. A new static field called SetupOperatingMode is also added to the SimpleDeviceService interface. This field contains the control ID.
Listing 11. Code generated a measurement
private final CommandMeasurement SetupOperatingMode =
new CommandMeasurement(
SimpleDeviceService.SetupOperatingMode, null, null, null,
SetupOperatingModeReport,SetupOperatingModeGetRequest,
SetupOperatingModeSetRequest);
|
The application code required to use the SetupOperatingMode measurement to get and set the setup.operating_mode variable on the INfinity 510 is shown in Listing 12.
Listing 12. Application code to use a measurement
// define the variable to hold the control
MeasurementService setupOperatingModeMeasurement;
// get the control
setupOperatingModeMeasurement =
device.getCommand(SimpleDeviceService.SetupOperatingMode);
String mode = null;
// perform a synchronous read
mode = (String) setupOperatingModeMeasurement.read(1000);
// set the operating mode to autonomous
setupOperatingModeMeasurement.executeWrite("autonomous");
// perform an asynchronous read
setupOperatingModeMeasurement.executeRead();
// get the measurement value
mode = (String) setupOperatingModeMeasurement.getValue()
|
Building a device for the Sirit INfinity 510 reader
In this example we'll guide you through the steps necessary to create and test a device for a Sirit INfinity 510 RFID reader. You'll learn how to model the commands necessary to tell the reader to start and stop reading and to send tag read reports. Once you've modeled the commands in CML and regenerated the device code, you'll write the application code necessary to use the generated controls to read tags.
You'll need the following items to complete this exercise:
- WebSphere Studio Device Developer Version 5.1.7, installed with the WebSphere RFID Tracking Kit 1.1.1
- Sirit INfinity 510 reader
- INfinity 510 Protocol Reference Guide
- The SiritTransport you created in Part 2.
The completed example code is in the Downloads section.
Add the transport classes to the wizard
You'll need the SiritTransport project created in the Part 2 to test your device code. You can specify the transport classes in the device CML file or when you create the device using the Device Kit Creation wizard. The Device Kit Creation wizard has two drop-down boxes that allow you to select the transport service and implementation class. The wizard does not automatically populate the drop-down boxes with the transport service and implementation classes loaded in the workspace. To add these classes to the wizard you'll need to update the Device Kit preferences, as follows:
- Select Window => Preferences.
- Expand Device Kit and select Devices.
- Add the transport service and implementation classes:
devworks.example.transport.service.SiritTransportServicedevworks.example.transport.SiritTransport
In this part of the example, you'll use the Device Kit Device Creation wizard to generate a skeleton device for the Sirit INfinity 510 reader. This device will use the SiritTransport created in a previous exercise. To generate the device:
- Select File => New => Other.
- Select Device Kit, then select Device.
- Click Next.
- Fill in the following fields, as shown in Figure 2:
- Name:
Sirit - Package Base:
devworks.example - Transport Service:
devworks.example.transport.service.SiritTransportService - Transport Implementation:
devworks.example.transport.SiritTransport - Select all of the generation options.
- Click Finish to generate the device.
Figure 2. Device Creation wizard
The Device Kit Device Creation wizard generates two Java projects. The SiritDevice project contains the device code and the SiritDeviceTest project contains a device test application. The generated device CML file, SiritDevice.cml, is located in the SiritDeviceDevelopment folder in the SiritDevice project. All the work you'll do in this exercise you'll do by modifying the SiritDevice.cml file and regenerating the device code using the Device Kit tools.
The INfinity 510 uses two channels for communication: a command/response channel and a channel for receiving asynchronous events. All communication with these channels is handled by the transport we built in Part 2.
The INfinity 510 reader's native protocol is ASCII text based. Every command is terminated using the character sequence\r\n. The corresponding reader response will end with the character sequence \r\n\r\n. An example command is
setup.region\r\n and a corresponding response is fcc\r\n\r\n.
Events are also terminated with the character sequence with \r\n\r\n. An example event is event.tag.report tag_id=0xAABBCCDDEEFFGG\r\n\r\n.
There are several different commands that can be used to read tags on the INfinity 510. We want to take advantage of the reader's asynchronous event reporting capability to have it send a notification whenever a tag is read. To do this, we'll need a command to tell the reader to start reading, a command to tell the reader to stop reading, a command to register for the tag report event and a command to unregister for the tag report event. The setup.operating_mode variable discussed earlier (see Measurement example) sets the operational mode of the reader to one of the following: autonomous, polled, or standby. When the mode is set to autonomous or polled, the reader continuously tries to singulate tags. If the mode is set to autonomous, the reader automatically reports any singulated tag via an asynchronous event notification on the event channel. When the mode is set to standby, the reader doesn't try to singulate tags unless instructed to do so with another tag read command. Setting the operational mode to autonomous instructs the reader to start reading and setting it to standby tells it to stop. The additional commands and events we'll need to model are:
tag.reporting.report_fieldsreader.register_eventreader.unregister_eventevent.tag.report
Model the setup.operating_mode measurement
We modeled the setup.operating_mode variable as a measurement earlier in this article. All we need to do now is add the CML definitions in Listing 10 to the SiritDevice.cml file and regenerate the device code. Add the definitions after the <description> element. To regenerate the device code:
- Close the Java editor for the
SiritDeviceclass. - Right-click SiritDevice.cml, and select Device Kit => Generate.
Test the setup.operating_mode measurement
Now that we've defined a measurement for the setup.operating_mode variable, let's test it to see if it works. In this test, we'll write the application code necessary to:
- Set the
setup.operating_modevariable toautonomous. - Get and print out the
setup.operating_modevariable value. - Set the
setup.operating_modevariable tostandby. - Get and print out the
setup.operating_modevariable value.
As previously mentioned, the Device Kit Device Creation wizard generates a device test project when it creates the device. Each device test project includes a class that extends com.ibm.esc.device.test.DeviceTest, and that can be used to test the device. The test class iterates over the device's controls and calls the methods appropriate for that control to test it. For example, if the control is an instance of CommandService, the test class will call the command's execute() method.
The default test behavior is limited in its ability to test a device. The test class does not, for example, call a command's execute(Object) method, because it doesn't know what value should be used to test the command. In this example, we'll override the default test behavior by making the following changes to the SiritDeviceTest class.
- Add a field definition to hold the
DeviceServiceobject:private DeviceService device = null;
- Override the
run()method inSiritDeviceTest. Call the superclassrun()method. - The
setDevice(DeviceService device)method inDeviceTestloops through the list of controls adding the appropriate listener for each control. We want to test that our application code can successfully add and remove listeners. To do this, override the method as shown:public void setDevice(final DeviceService device) { this.device = device; if (isActive()) { test(); } }
- Override the
getDevice()method:public DeviceService getDevice() { return this.device; }
- Add the following properties and values to the end of the esc.properties file:
siritdevicetest.testcount=0 sirittransport.host=ip_address_of_your_reader
Now that we've overridden the default test behavior, we need to add the application logic to test the SiritDevice. We need to add a field to the SiritDeviceTest class to hold the setup.operating_mode measurement, and then add logic to the run() method that uses the control to get and set the setup.operating_mode variable on the reader. The code we need to add to the SiritDeviceTest class is shown in Listing 13.
Listing 13. Code to test the setup.operating_mode measurement
// control field definition
MeasurementService setupOperatingModeMeasurement;
// run() method updates
// get the control
setupOperatingModeMeasurement =
getDevice().getMeasurement(SiritDeviceService.SetupOperatingMode);
// set the property setup.operating_mode variable to autonomous
setupOperatingModeMeasurement.executeWrite("autonomous");
// get and print out the operating mode
setupOperatingModeMeasurement.executeRead();
sleep(1000);
log(LOG_INFO, "operating mode = " +
setupOperatingModeMeasurement.getValue());
// set the property setup.operating_mode variable to standby
setupOperatingModeMeasurement.executeWrite("standby");
// get and print out the operating mode
setupOperatingModeMeasurement.executeRead();
sleep(1000);
log(LOG_INFO, "operating mode = " +
setupOperatingModeMeasurement.getValue());
|
Now we're ready to find out if we've modeled the measurement correctly and if the SiritDevice works. To test the device using the modified SiritDeviceTest class:
- Select the SiritDeviceTest project in a Package Explorer view.
- Select Run => Run As => Java Application.
When the SiritDeviceTest application runs, you should see the following output written to the console:
[INFO] 2006-12-19 01:32:36.741 - operating mode = autonomous [INFO] 2006-12-19 01:32:37.192 - operating mode = standby |
Model the tag.reporting.report_fields measurement
The tag.reporting.report_fields variable is used to tell the INfinity 510 what tag fields should be reported on the event.tag.report event. The possible values are:
tag_id(tag ID or EPC)tiduser_datatype(protocol type)timeantennafreqrssitx_power
The format of the tag.reporting.report_fields command is tag.reporting.report_fields=fields\r\n, where fields is set to one or more of the valid tag field values. Add the CML definitions in Listing 14 to the SiritDevice.cml file and regenerate the device code.
Listing 14. CML to model the tag report fields measurement
<!-- Define the command to get the TagReportingReportFields value -->
<command id="TagReportingReportFieldsGetRequest">
<message id="TagReportingReportFieldsGetRequestMessage">
<ascii>tag.reporting.report_fields\r\n</ascii>
</message>
</command>
<!-- Define the command to set the TagReportingReportFields value -->
<command id="TagReportingReportFieldsSetRequest">
<message id="TagReportingReportFieldsSetRequestMessage">
<ascii>tag.reporting.report_fields = \r\n</ascii>
<parameter type="string">
<insert/>
<index>30</index>
</parameter>
</message>
</command>
<!-- Define the TagReportingReportFieldsReport signal -->
<signal id="TagReportingReportFieldsReport">
<message id="TagReportingReportFieldsReportMessage">
<ascii>ok \r\n\r\n</ascii>
<parameter type="string">
<insert/>
<index>3</index>
</parameter>
<filter>
<bytes format="hex">ff,ff,ff</bytes>
</filter>
<sentmessage idref="TagReportingReportFieldsGetRequestMessage"/>
</message>
</signal>
<!-- Define the TagReportingReportFields measurement. -->
<measurement id="TagReportingReportFields">
<readcommand idref="TagReportingReportFieldsGetRequest"/>
<writecommand idref="TagReportingReportFieldsSetRequest"/>
<signal idref="TagReportingReportFieldsReport"/>
</measurement>
|
Test the tag.reporting.report_fields measurement
Now that we've defined a measurement for the tag.reporting.report_fields variable, let's test it to see if it works. In this test, we'll write the application code necessary to:
- Set the
tag.reporting.report_fieldsvariable totag_idandantenna. - Get and print out the
tag.reporting.report_fieldsvariable.
We need to add a field to the SiritDeviceTest class to hold the tag.reporting.report measurement, and then add logic to the run() method that uses the control to get and set the tag.reporting.report variable on the reader. The code we need to add to the SiritDeviceTest class is shown in Listing 15.
Listing 15. Code to test the tag report fields measurement
// control field definition
MeasurementService tagReportingReportFieldsMeasurement;
// get the control
tagReportingReportFieldsMeasurement =
getDevice().getMeasurement(SiritDeviceService.TagReportingReportFields);
// set the tag reporting fields
tagReportingReportFieldsMeasurement.executeWrite("tag_id antenna");
// get and print out the tag reporting fields
tagReportingReportFieldsMeasurement.executeRead();
sleep(1000);
log(LOG_INFO, "tag reporting fields = " +
tagReportingReportFieldsMeasurement.getValue());
|
To test your changes, run the SiritDeviceTest application. You should see the following output written to the console:
[INFO] 2006-12-19 02:43:04.571 - tag reporting fields = tag_id antenna |
Model the reader.register_event and reader.unregister_event commands
The commands to register and unregister for events are reader.register_event and reader.unregister_event . The formats of these commands are:
reader.register_event (name = event)\r\n, whereeventis the event you want to register an interest in.reader.register_event (name = event)\r\n, whereeventis the event you are no longer interested in.
The event we want to register and unregister an interest in is event.tag.report. When you model this command in CML, you have a choice of either creating a concrete or template message. Since event.tag.report is the only event we're interested in, we'll model the commands using concrete messages (if we were modeling more events, we would use a template message here). Add the CML definitions in Listing 16 to the SiritDevice.cml file and re-generate the device code.
Listing 16. CML to model the event registration commands
<!-- Define the EventTagReportRegisterRequest command -->
<command id="EventTagReportRegisterRequest">
<message id="EventTagReportRegisterRequestMessage">
<ascii>reader.register_event(name = event.tag.report)\r\n</ascii>
</message>
</command>
<!-- Define the EventTagReportUnregisterRequest command -->
<command id="EventTagReportUnregisterRequest">
<message id="EventTagReportUnregisterRequestMessage">
<ascii>reader.unregister_event(name = event.tag.report)\r\n</ascii>
</message>
</command>
|
Model the event.tag.report event signal
The last thing we need to model is the event.tag.report event. The INfinity 510 automatically sends out the event.tag.report event for singualted tags when the reader is operating in autonomous mode. Since events are notifications sent by the reader, we'll need to model the event using a signal.
The format of the event.tag.report event is event.tag.report tag_field=value tag_field=value\r\n, where tag_field is one of the fields specified in the tag.reporting.report_fields variable. For example, if the tag.reporting.report_fields variable is set to tag_id antenna, the event.tag.report event would resemble event.tag.report tag_id=0xAABBCCDDEEFFGG antenna=1\r\n.
The CML to model the event.tag.report event as a signal is shown in Listing 17. Add the CML definitions to the SiritDevice.cml file and regenerate the device code.
Listing 17. CML to model the tag read event as a signal
<!-- Define the EventTagReportReport signal -->
<signal id="EventTagReportReport">
<message id="EventTagReportReportMessage">
<ascii>event.tag.report \r\n\r\n</ascii>
<parameters type="Map">
<parameter>
<key>tag_id</key>
<field>tag_id</field>
</parameter>
<parameter>
<key>type</key>
<field>type</field>
</parameter>
<parameter>
<key>antenna</key>
<field>antenna</field>
</parameter>
<parameter>
<key>tid</key>
<field>tid</field>
</parameter>
<parameter>
<key>time</key>
<field>time</field>
</parameter>
<parameter>
<key>freq</key>
<field>freq</field>
</parameter>
<parameter>
<key>rssi</key>
<field>rssi</field>
</parameter>
<parameter>
<key>tx_power</key>
<field>tx_power</field>
</parameter>
<parameter>
<key>user_data</key>
<field>user_data</field>
</parameter>
</parameters>
<filter>
<bytes format="hex">ff, ff, ff, ff, ff, ff, ff, ff,
ff, ff, ff, ff, ff, ff, ff, ff, ff</bytes>
</filter>
</message>
</signal>
|
Now that we've modeled all the necessary commands and events, we're ready to read a tag. To read a tag, we'll update the SiritDeviceTest class to do the following:
- Set the
tag.reporting.report_fieldsvariable totag_id antenna. - Add a listener to the
event.tag.reportsignal. - Register an interest in the
event.tag.reportevent. - Set the
setup.operating_modevariable toautonomous. - Read tags for 1 second.
- Set the
setup.operating_modevariable tostandby. - Unregister an interest in the
event.tag.reportevent. - Remove the listener for the
event.tag.reportsignal. - Add a
signalOccurred(SignalService, Object, Object)method to process theevent.tag.reportevents.
The field definitions and code for the run() and signalOccurred(...) methods are shown in Listing 18. Add this code to SiritDeviceTest.
Listing 18. Code to test tag reading
MeasurementService setupOperatingModeMeasurement;
MeasurementService tagReportingReportFieldsMeasurement;
CommandService eventTagReportRegisterRequestCommand;
CommandService eventTagReportUnregisterRequestCommand;
SignalService eventTagReportReportSignal;
public void run() {
super.run();
// get the controls
tagReportingReportFieldsMeasurement = getDevice().getMeasurement(
SiritDeviceService.TagReportingReportFields);
setupOperatingModeMeasurement = getDevice().getMeasurement(
SiritDeviceService.SetupOperatingMode);
eventTagReportRegisterRequestCommand = getDevice().getCommand(
SiritDeviceService.EventTagReportRegisterRequest);
eventTagReportUnregisterRequestCommand = getDevice().getCommand(
SiritDeviceService.EventTagReportUnregisterRequest);
eventTagReportReportSignal = getDevice().getSignal(
SiritDeviceService.EventTagReportReport);
// set the tag reporting fields
tagReportingReportFieldsMeasurement.executeWrite("tag_id antenna");
// add a listener for the event.tag.report signal
eventTagReportReportSignal.addSignalListener(this);
// register for the event.tag.report event
eventTagReportRegisterRequestCommand.execute();
// set the operating mode of the reader to autonomous
setupOperatingModeMeasurement.executeWrite("autonomous");
// read for 1 second
sleep(1000);
// set the operating mode of the reader to standby
setupOperatingModeMeasurement.executeWrite("standby");
// unregister for the event.tag.report event
eventTagReportUnregisterRequestCommand.execute();
// remove the listener for the event.tag.report signal
eventTagReportReportSignal.removeSignalListener(this);
}
public void signalOccurred(SignalService source,
Object timestamp, Object data) {
log(LOG_INFO, " >>>> Signal occurred..." + " signal: " +
source.getKey() + " data: " + data);
}
|
Now let's see if you can read a tag. Place one or more tags near the reader antenna and run the SiritDeviceTest application. If your code is working correctly, you should see output similar to that shown below written to the console:
[INFO] 2007-01-29 13:21:54.156 - >>>> Signal occurred... signal:
EventTagReportReport data: {antenna=1, tag_id=0x30144B5A1CC3758000000008, type=ISOC}
[INFO] 2007-01-29 13:21:54.359 - >>>> Signal occurred... signal:
EventTagReportReport data: {antenna=1, tag_id=0x30144B5A1CC3758000000009, type=ISOC}
[INFO] 2007-01-29 13:21:54.359 - >>>> Signal occurred... signal:
EventTagReportReport data: {antenna=1, tag_id=0x30144B5A1CC375800000000D, type=ISOC}
[INFO] 2007-01-29 13:21:54.359 - >>>> Signal occurred... signal:
EventTagReportReport data: {antenna=1, tag_id=0x30144B5A1CC3758000000008, type=ISOC}
[INFO] 2007-01-29 13:21:54.359 - >>>> Signal occurred... signal:
EventTagReportReport data: {antenna=1, tag_id=0x30144B5A1CC375800000000E, type=ISOC}
|
In this article, you learned about the device component of a Device Kit adapter, including the CML definitions required to model a hardware device's command set and the resulting control classes that get created from the definitions. You followed the steps to create a device for a Sirit INfinity 510 RFID reader, including modeling the commands and events necessary to tell the reader to start and stop reading, and to report tag read events. In the final article in this series, you'll build on this knowledge to create a new reader agent for the INfinity 510 that you can plug into the WebSphere RFID solution.
| Description | Name | Size | Download method |
|---|---|---|---|
| Completed sample device | device_projects.zip | 42KB | HTTP |
Information about download methods
Learn
-
Developing a device adapter and agent for the WebSphere RFID solution, Part 1: Introduction to the WebSphere RFID Device Kit (developerWorks, 2007): Describes the functions provided by the WebSphere RFID Device Infrastructure component and how to construct device adapters using the WebSphere RFID Device Infrastructure Device Development Kit.
-
Developing a device adapter and agent for the WebSphere RFID solution, Part 2: Building the transport component (developerWorks, 2007): Learn how to begin implementing an adapter with the WebSphere RFID Device Kit, starting with the transport layer and using the Sirit INfinity 510 reader as an example.
-
The Sirit INfinity 510 User's Guide: Everything you need to know about how to install, configure, and use the reader.
-
Websphere RFID Premises Server product overview: A brief overview of the Websphere RFID solution.
-
WebSphere RFID Information Center: Product documentation for the Websphere RFID solution.
-
Websphere Studio Device Developer help has information for all the RFID Tracking Kit components
(after you install the RFID Tracking Kit feature)
-
The Sirit INfinity 510 Protocol Reference Guide v1.0
will soon be available at http://www.sirit.com
-
Print simple and complex RFID labels with the WebSphere RFID solution (developerWorks, Nov 2006): A guide to printing RFID labels with the Websphere RFID solution.
-
Redbook: IBM WebSphere RFID Handbook: A Solution Guide
-
developerWorks Wireless with WebSphere zone
Get products and technologies
-
Download a trial version of WebSphere Studio Device Developer
-
Install the RFID Tracking Kit into Device Developer using the Update Manager and defining a site bookmark to
http://www-306.ibm.com/software/pervasive/wsdd/updates/571/rfid

Allen Smith is a Senior Software Engineer with the Pervasive Computing Group in Research Triangle Park, North Carolina. He works with business partners to design solutions that use IBM's pervasive middleware. You can contact Allen at allens@us.ibm.com.
Comments (Undergoing maintenance)






