Please note that these Trac pages are no longer being updated. Wiki contents/documentation have moved to GitHub.

Using Sensors In Seattle

A Seattle / Repy program cannot access resources like GPS, wifi signal strength information, motion sensors, etc. However, many users would like to provide information about resources like this to researchers. To facilitate the exchange of information, we provide an API that sensor applications (installed separately from Seattle) can use to make the sensor available to local Repy programs.

Overview

The API that is provided by sensors may vary significantly from application to application. The philosophy is to provide an API that is as simple as possible but would allow a variety of sensor applications to operate. We intentionally under constrain the interface to make it as easy as possible to use in the base case. Further parts of this document describe how to do slightly more advanced functionality while providing this API.

Required API

All sensors must provide an XMLRPC interface bound to 127.0.0.1 on any port from 63090-63099. This XMLRPC interface must support at least the following two calls: isSeattleSensor(), system.listMethods(), and system.methodSignature. (See  the SimpleXMLRPCAPI for general details on Python, but similar libraries exist on other platforms.) The isSeattleSensor() call must return True. The system.listMethods() should list the available calls. The system.methodSignature(callname) must return the calling arguments for a function.

Note: it is completely fine for a sensor to hide calls it does not want to be called by general clients. In other words, if you have a call that performs a potentially disruptive act, you need not list it. Keep in mind that this is not a security mechanism. It is just to prevent a simple and general client from calling it without understanding what it does.

It is also recommended that you provide the call system.methodHelp. This is not required and clients must not rely on their existence.

Example Use

Here is some example pseudo-code for building a sensor and client.

Sensor code:

import SimpleXMLRPCServer
import socket
import sys

for sensorport in [63095, 63096, 63097, 63098, 63099, 63090, 63091, 63092, 63093, 63094]:
  try:
    server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", sensorport))
    server.register_introspection_functions()
  except socket.error:
    print 'port:',sensorport,'failed.   Trying backup port...' 
    continue
  print "Listening on port",sensorport,"..." 
  break
else:
  print 'Could not locate a port!'
  sys.exit(1)
    
def isSeattleSensor():
  """ isSeattleSensor() always returns True"""
  return True

def mySensorName():
  """ my sensor name is "Hello World Sensor"."""
  return "Hello World Sensor"

def readSensor():
  """ Read data from the hello world sensor"""
  return "Hello World!"

def readSensorForLanguage(language):
  """ Read data from the hello world sensor in a few languages.   Pass in a string."""
  if language == 'English':
    return "Hello World!"
  elif language == 'German':
    return "Hallo Welt!"
  elif language == 'Mandarin':
    return "Ni Hao!"
  else:
    raise Exception("Unsupported language")

# NOTE: the system.* calls are defined by the SimpleXMLRPCServer in Python by calling the following.   Your library's use may vary.
server.register_introspection_functions()

server.register_function(mySensorName)
server.register_function(readSensor)
server.register_function(readSensorForLanguage)
server.register_function(isSeattleSensor)

try:
    print 'Use Control-C to exit'
    server.serve_forever()
except KeyboardInterrupt:
    print 'Exiting'

Repy sensor reading code:

include xmlrpc_client.repy

if callfunc=="initialize":
  for sensorport in [63095, 63096, 63097, 63098, 63099, 63090, 
    63091, 63092, 63093, 63094]:
    try:
      s = xmlrpc_client_Client('http://localhost:'+str(sensorport))
    except socket.error:
      print 'port:', sensorport, 'failed.   Trying backup port...' 
      continue
    print "Connected to port", sensorport
    break
  else:
    print 'Could not locate a port!'
    exitall()

  # should check the error status...
  try: 
    if not s.send_request("isSeattleSensor", ()):
      print 'something other than a Seattle sensor was bound to the port...'
      exitall()
  except Exception, e:
    print 'Got an error when checking if this is a Seattle sensor:', str(e)
    exitall()

  # Get the list of available methods
  methodlist = s.send_request("system.listMethods", ())

  for methodname in methodlist:
    # NOTE: Wrap the methodname string inside a tuple, otherwise
    # xmlrpc_common will treat the string's characters as separate params
    thissignature = s.send_request("system.methodSignature", (methodname,))

    # BUG: This is true for all signatures, but shouldn't be.
    if thissignature == 'signatures not supported':
      print 'Method:', methodname, 'has an unknown signature.'
    else:  
      print 'Method:',methodname,'has the signature', thissignature

    if thissignature == ():
      print 'Empty call returns:', s.send_request(methodname, ())

  print s.send_request("isSeattleSensor", ())
  print s.send_request("mySensorName", ())
  print s.send_request("readSensor", ())
  print s.send_request("readSensorForLanguage", ('Mandarin',))

Sensors on Android

Smartphones often come with a number of embedded sensors and chips, such as an accelerometer, GPS, Wifi card, etc. New sensing applications therefore can turn an everyday phone into a smart sensor. As we have Seattle ported on Android platform (SeattleOnAndroid), here is a brief introduction how to provide APIs that an Android application can use to make the sensor available to Seattle on Android.

XMLRPC Library

Python has a set of quick and easy  the SimpleXMLRPCAPI available. However, Android Java applications have to import their own library.  Apache WS-XMLRPC is one of the most popular Java XMLRPC libraries. Here are a few steps how to import it, and create a light weight SimpleXmlRPcServer for Android.

Importing Apache

Download the Apache library from  Apache WS-XMLRPC. The jar files in /apache-xmlrpc-version-number/lib/ are what you need. Create a new folder libs in your Eclipse Android project (the libs folder will be automatically created by Eclipse with higher version (>15) of Android API).

Right click libs and choose Import -> General -> File System, then Next. Browse in the filesystem to find the library's parent directory (i.e., /apache-xmlrpc-version-number/lib/). Click the directory name, then check the relevant jar files in the right pane. This puts the library physically into your project.

Right click on your Android project, choose properties -> "Java Build Path" tab, click the Libraries tab, then "Add JARs...", navigate to the new jar files you just imported in the libs directory and add it. They should now appear under “Referenced Libraries” in your project. This is the moment when your new jar files are converted for use on Android.

SimpleXmlRPcServer for Android

 zen developed a simple and light weight SimpleXmlRPcServer using the above Apache library, which allows us to very quickly craft a standalone, simple XMLRPC server using the above Apache library. No servlet or container is required.

Two classes are needed for the server, SimpleXmlRPcServer and SimpleXmlRPcRequestHandlerFactory on zen's page. Simply place these two classes in your project then you are ready to go (in case the website goes down in the future, I attached the code below. Remember to replace the first line with your own package name). According to zen, the code "supports single instance handler object, but not yet multi-threaded".

Example Use

Here are some example code for building Android sensor (XMLRPC server) and a client.

Sensor code:

public class RpcServer extends IntentService {
        private int[] portArray = new int[] {63090, 63091, 63092, 63093, 63094, 63095, 63096, 63097, 63098, 63099};
	private int port = 0;
        .........
        
        @Override
	protected void onHandleIntent(Intent intent) {	
                SimpleXmlRpcServer server;
                int i;
		for (i = 0; i < portArray.length; i++) {
			port = portArray[i];
			try {
				server = new SimpleXmlRpcServer(port);
				server.addHandler("yanyan", new SimpleXMLRpcTest());
				server.serve_forever();
				Toast.makeText(context, "Server Listening on: " + Integer.toString(port), Toast.LENGTH_LONG).show();
				break;
			} catch (Exception e) {
				String text = Log.getStackTraceString(e);
				Log.e("RpcServer Exception", text);
			}
		}		
		if (i == portArray.length) {
                        Log.e("RpcServer Exception", "Could not locate a port!");
		}
	}
}

public class SimpleXMLRpcTest {

	public String hello(){
		return "hello world";
	}
 
        public boolean isSeattleSensor() {
                return true;
        }
}	

Sensor reading code:

XmlRpcClientConfigImpl config = new XmlRpcClientConfigImpl();
int i;
for (i = 0; i < portArray.length; i++) {
	port = portArray[i];
	try {
		config.setServerURL(new URL("http://127.0.0.1:" + Integer.toString(port)));
		XmlRpcClient client = new XmlRpcClient();
		client.setConfig(config);
		Object[] params = new Object[]{};

		String result = (String) client.execute("yanyan." + hello, params);
		response = result;
		Log.d("ClientActivity got response ", result);
		break;
	} catch (XmlRpcException e) {
		String text = Log.getStackTraceString(e);
		Log.e("RpcClient Exception", text);
	} catch (MalformedURLException e) {
		if (e.getMessage().contains("refused")) continue;
	} 
}

if (i == portArray.length) {
	Log.e("ClientActivity", "Could not locate a port!");
}

In sensor code server.addHandler("yanyan", new SimpleXMLRpcTest()), the string label for the handler can be anything. But the client code client.execute("yanyan." + hello, params) has to use the same string label. Another important thing is, XMLRPC can only work on a normal Java class, not Activity or Service. Therefore I made a class SimpleXMLRpcTest outside RPcServer, which extends IntentService?. The client code can be placed in Activity and has no constraints.

Attachments