Managing Devices with XML-RPC

Brett presents a client-side monitor application (written in Python) that uses XML-RPC to monitor the state of a simulated device.


April 01, 2003
URL:http://www.drdobbs.com/web-development/managing-devices-with-xml-rpc/184405319

Apr03: Managing Devices with XML-RPC

Brett is chief architect, device management framework, for Art & Logic Inc. He can be contacted at [email protected].


Developers routinely encounter a variety of interfaces and protocols when designing and building devices. For instance, manufacturers of embedded devices have historically provided simple command-line interfaces to monitor and control their products, while in the networking world, vendors often resort to SNMP interfaces. In other industries, you run into devices with protocols ranging from Sun's ONC-RPC or CORBA to Java RMI or custom homebrewed binary protocols.

More recently, designers of these systems have started integrating web-based management interfaces. In addition to being ideal for occasional users or consumer products, these systems employ a protocol that provides a remote API to devices, allowing the development of applications that are not bound by web browser limitations. The DMF Embedded Web Application Server (developed by Art & Logic, the company I work for; http://www.artlogic.com/dmf/) is one such tool that lets you build traditional web-based device management interfaces and open up devices as web services.

Some applications of web service-enabled devices are:

In this article, I present a monitor application (written in Python) that uses XML-RPC to monitor the state of a simulated device. The DMF Embedded Web Application Server implements the server-side functionality required for the client-side Python application and assumes that a device exists that implements the XML-RPC API discussed here. (The complete source code to the application is available electronically; see "Resource Center," page 5.) Some of the advantages for using XML-RPC or SOAP instead of CORBA, SNMP, RMI, or other legacy protocols are:

While this article focuses on using XML-RPC to monitor and control devices, you could just as easily use SOAP (XML-RPC's sibling protocol). In fact, SOAP may be a preferable protocol in some cases, depending on tool support (for instance, if you plan on writing clients with one of the .NET languages or another tool that speaks SOAP natively).

Parameter Space

Central to the design of the monitor application I present here is the decision to view the state of the device as a tree of named parameters. Each of these parameters has a value and separate read/write access controls. While it's not required as part of the design, I usually model the parameter space as a hierarchical tree. One advantage of a hierarchical parameter space is that the DMF's XML-RPC implementation answers a request for a node in the tree by returning the values of all the parameters beneath that node, letting you condense several (or many) HTTP transactions with a single call/response cycle.

For example, you might design a device that has a node at the root of its parameter tree named "sys." Under that you could build a tree like the following:

sys

info

sysName

uptime

description

buildDate

network

interfaceCount

ipAddress

ipMask

...and so on.

Requesting the value of sys.info returns the values of the parameters sys.info.sysName, sys.info.uptime, sys.info.description, and sys.info.buildDate as a single XML-RPC structure. Parameters at the leaves of the tree may be indexable. For instance, there might be valid values for the parameters sys.network.ipAddress[0] through sys.network.ipAddress[sys.network.interfaceCount-1].

The XML-RPC API

Since the entire state of the device is represented by a tree of named parameters, an API with get/set semantics is all that's needed to monitor and control the device. Example 1 is pseudocode for the two main functions in the XML-RPC interface.

When an error occurs in XML-RPC, the server notifies the client by sending a special Fault message to notify the client code of the problem. The Fault message contains a structure with two members: an application-defined fault code and a text description of the error.

The Python xmlrpclib module converts fault messages arriving from the server into an exception object that you can catch, keeping the main flow of the client app logic clean; see Example 2.

The Python Device Class

I use two Python classes to abstract the remote device in the client. The main class, Device, provides three major API functions:

The ValueTree class stores data from the device in the same nested dictionary data structure used by the xmlrpclib. Doing this lets you swap in new subtrees in a single step. The main API functions exposed by the ValueTree class are value = GetValue(parameterName [,index]) and SetValue(parameterName, value [, index]), which retrieve or set the requested value in the ValueTree's set of nested dictionaries. The Device class's GetValue() function just delegates to the implementation of ValueTree's GetValue() function.

The Pseudodevice

For this client-side monitor demonstration, I've built a simulated device with a small (but interesting) parameter tree. The pseudodevice contains two subtrees that branch from the top level: system.info and system.values. The system.info branch contains parameters that provide information about the device itself:

The system.values branch of the parameter tree contains the actual data values reported by the device, as well as additional information about the data values. I've modeled this application after several real-world projects I've worked on—monitoring satellite telemetry data, broadcasting equipment at FM radio stations, and the like. Each of the data value parameters also has an associated text label and separate highAlarm and lowAlarm alarm values. When the data value is either lower than its lowAlarm value or higher than its highAlarm alarm value, it is considered to be in an alarm state. Each of these parameters is indexable; calling server.get("system.values.value") returns the entire array in a single XML-RPC array, or you may also call server.get("system.values.value", 0) to retrieve just the first data value. The full list of parameters contained in the system.values subtree is:

The Monitor Application

The monitor application uses the Device class to monitor the simulated pseudodevice. The simulator is written to initialize each of the 16 data values randomly between 1 and 100, then perform a simple random walk around each of those data values every time an update is requested.

The application interface was written using the Tkinter toolkit, standard with Python. The application should build and run cleanly on any platform supported by Python.

As Figure 1 shows, each data value on the device is represented in the UI as a single row within a grid. Each row displays that value's associated label, lower/upper alarms, and current value. The label and alarm values are editable; moving the cursor out of a modified cell causes the app to transmit the new label or alarm value to the device. The current value is displayed as read-only. When the current value is lower than the lower alarm setting for that channel, it is displayed as yellow text on a blue background; if it is higher than the upper alarm, it is displayed as white text on a red background. Every second, the monitor application queries the device for the pertinent section of its parameter tree and updates the display accordingly.

Potential Drawbacks of XML-RPC

While I've shown here the relative ease with which you can build a standalone application to manage a device using XML-RPC, there are aspects of the protocol that you must consider before adopting it for real projects.

DDJ

Apr03: Managing Devices with XML-RPC


   value = get(parameterName [, index]);
      where
         value: Value or values associated with the 
                requested parameter or parent node.
         parameterName: name of the desired parameter 
                or parent node.
         index: optional, used to retrieve indexed 
                parameter values
   set(parameterName, [index,] value);
      where
         parameterName: name of the desired parameter. 
         index: optional, used to set indexed parameter values
         value: new value for the specified parameter. The set()
         operation only works on individual parameter values.

Example 1: Pseudocode for the two main functions in the XML-RPC interface.

Apr03: Managing Devices with XML-RPC


import xmlrpclib
server = xmlrpclib.ServerProxy("http://yourAddressHere/RPC2")
try:
   value = server.get("sys.info.sysName")
   print "sys.info.sysName = ", value
except xmlrpclib.Fault, f
   print "XML-RPC Fault: ", f

Example 2: Converting fault messages arriving at the server.

Apr03: Managing Devices with XML-RPC

Figure 1: Monitor application in action.

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.