ch.epfl.gsn.wrappers.tinyos.MigMessageWrapper Maven / Gradle / Ivy
The newest version!
/**
* Global Sensor Networks (GSN) Source Code
* Copyright (c) 2006-2016, Ecole Polytechnique Federale de Lausanne (EPFL)
*
* This file is part of GSN.
*
* GSN is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GSN is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GSN. If not, see .
*
* File: src/ch/epfl/gsn/wrappers/tinyos/MigMessageWrapper.java
*
* @author Ali Salehi
* @author Mehdi Riahi
*
*/
package ch.epfl.gsn.wrappers.tinyos;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.tinyos.message.Message;
import net.tinyos.message.MoteIF;
import net.tinyos.packet.BuildSource;
import net.tinyos.util.PrintStreamMessenger;
import org.slf4j.LoggerFactory;
import ch.epfl.gsn.beans.AddressBean;
import ch.epfl.gsn.beans.DataField;
import ch.epfl.gsn.wrappers.AbstractWrapper;
import org.slf4j.Logger;
/** MigMessageWrapper is used for reading TinyOS 2.x generated data packets.
* The wrapper reads packet definition from a MIG-generated .java-file and
* uses the compiled class-file to read the appropriate data.
*/
public class MigMessageWrapper extends AbstractWrapper implements net.tinyos.message.MessageListener {
// regular expression clauses for finding the field names and their types from packet definition
private static final String NAME_PATTERN = "// Accessor methods for field: (\\w+)";
private static final String TYPE_PATTERN = "//\\s+Field type: (\\w+(\\[\\])?),";
private static final String INITPARAM_SOURCE = "source";
private static final String INITPARAM_PACKETNAME = "packet-name";
private static final String INITPARAM_PATH = "path";
private static final String INITPARAM_CLASS_PACKAGE = "class-package";
private String source;
private String packetName;
private String path;
private String classPackage;
private int threadCounter = 0;
private final transient Logger logger = LoggerFactory.getLogger( MigMessageWrapper.class );
private transient DataField [ ] outputStructureCache;
// the interface to communicate with mote
private MoteIF moteIF;
private static HashMap types;
// hashmap for storing the field name and its type
private HashMap fields;
private HashMap outputFields;
// arraylist for storing the field names in the correct order
private ArrayList fieldsOrdered;
private ArrayList outputFieldsOrdered;
// class and object variables used to extract data from packets using reflection
private Class> packetClass;
private Object packetObject;
public boolean initialize ( ) {
AddressBean addressBean = getActiveAddressBean( );
if ( addressBean.getPredicateValue(INITPARAM_SOURCE) != null ) {
source = addressBean.getPredicateValue(INITPARAM_SOURCE);
} else {
logger.warn("The specified parameter >" + INITPARAM_SOURCE + "< for >MigMessageWrapper< is missing.");
logger.warn("Initialization failed.");
return false;
}
if ( addressBean.getPredicateValue(INITPARAM_PACKETNAME) != null ) {
packetName = addressBean.getPredicateValue(INITPARAM_PACKETNAME);
} else {
logger.warn("The specified parameter >" + INITPARAM_PACKETNAME + "< for >MigMessageWrapper< is missing.");
logger.warn("Initialization failed.");
return false;
}
if ( addressBean.getPredicateValue(INITPARAM_PATH) != null ) {
path = addressBean.getPredicateValue(INITPARAM_PATH);
} else {
logger.warn("The specified parameter >" + INITPARAM_PATH + "< for >MigMessageWrapper< is missing.");
logger.warn("Initialization failed.");
return false;
}
if ( addressBean.getPredicateValue(INITPARAM_CLASS_PACKAGE) != null ) {
classPackage = addressBean.getPredicateValue(INITPARAM_CLASS_PACKAGE);
} else {
classPackage = "ch.epfl.gsn";
}
try {
logger.debug("Connecting to " + source);
moteIF = new MoteIF(BuildSource.makePhoenix(source, PrintStreamMessenger.err));
} catch(Exception e) {
logger.warn("Cannot open connection to: " + source, e);
return false;
}
types = makeTypeMap();
logger.debug("Reading packet definition from " + path + packetName + ".java");
try {
packetClass = Class.forName(classPackage + "." + packetName);
packetObject = packetClass.newInstance();
Message msg = (Message)packetObject;
moteIF.registerListener(msg, this);
}
catch (Exception e) {
logger.warn("Reading packet structure of " + packetName + " failed:", e);
return false;
}
outputStructureCache = createOutputStructure(path + packetName + ".java");
if(outputStructureCache == null)
return false;
else {
logger.warn("TinyOS MIG wrapper identifed the following fields from the "+(path+packetName+".java"));
for (DataField df : outputStructureCache)
logger.warn("\t FieldName: "+df.getName()+" ("+df.getType()+")");
}
return true;
}
/** Creates a HashMap, where key is the type in nesC and value is the type in GSN
*/
private static HashMap makeTypeMap() {
HashMap types = new HashMap();
types.put("byte", "tinyint");
types.put("short", "smallint");
types.put("int", "integer");
types.put("long", "bigint");
types.put("byte[]", "tinyint");
types.put("short[]", "smallint");
types.put("int[]", "integer");
types.put("long[]", "bigint");
return types;
}
/** Creates the output structure required for posting data to GSN
* Reads definition from a file, whose name is given as a parameter and
* uses regular expression
*/
private DataField[] createOutputStructure(String filename) {
fields = new HashMap();
fieldsOrdered = new ArrayList();
outputFields = new HashMap();
outputFieldsOrdered = new ArrayList();
try
{
BufferedReader input = new BufferedReader(new FileReader(filename));
String data;
Pattern namePattern = Pattern.compile(NAME_PATTERN);
Pattern typePattern = Pattern.compile(TYPE_PATTERN);
Matcher nameMatcher;
Matcher typeMatcher;
String name = null;
String type = null;
// Name and type aren't on the same line, so we have to clear the
// variables only after both are read successfully.
// There is a possibility that this might cause problems if
// at some time the regular expressions don't match the correct
// lines.
// The names have to be put to a separate datastructure, which
// retains the order: if they were stored only to a hashmap, then
// later we wouldn't know in which order they would be and the
// values would be set to wrong sensor names.
while((data = input.readLine()) != null) {
nameMatcher = namePattern.matcher(data);
typeMatcher = typePattern.matcher(data);
while(nameMatcher.find()) {
name = nameMatcher.group(1);
}
while(typeMatcher.find()) {
type = typeMatcher.group(1);
}
if(name != null && type != null) {
if(type.contains("[]")) {
int i=0;
int size=0;
Method getArraySize = packetClass.getDeclaredMethod("numElements_" + name);
try {
size = (Integer) getArraySize.invoke(packetObject);
} catch (InvocationTargetException e) {
logger.error("Invocation of numElements_" + name + " failed: %s%n", e);
}
for(i=0; i < size; i++) {
outputFieldsOrdered.add(name + i);
outputFields.put(name + i, type);
logger.debug("Adding " + name + i + " with type " + type);
}
fieldsOrdered.add(name);
fields.put(name, type);
name = null;
type = null;
} else {
fieldsOrdered.add(name);
fields.put(name, type);
outputFieldsOrdered.add(name);
outputFields.put(name, type);
logger.debug("Adding " + name + " with type " + type);
name = null;
type = null;
}
}
}
input.close();
}
catch (Exception e) {
logger.warn("File input error", e);
return null;
}
// create an array of DataTypes for returning
ArrayList fieldsAL = new ArrayList();
Iterator it = outputFieldsOrdered.iterator();
while(it.hasNext()) {
String curName = it.next();
String curType = outputFields.get(curName);
fieldsAL.add(new DataField(curName.toUpperCase(), types.get(curType), curName));
}
return fieldsAL.toArray(new DataField[] {} );
}
/** Implements net.tinyos.message.MessageListener.messageReceived
* Stores the received message to a buffer so that GSN can fetch the message
* from there whenever it wants.
*/
public void messageReceived(int to, Message message) {
logger.debug("Received message");
try {
Constructor> packetConstructor = packetClass.getDeclaredConstructor(Message.class, int.class, int.class);
if(message == null) {
logger.warn("Message was null!");
return;
}
packetObject = packetConstructor.newInstance(message, message.baseOffset(), message.dataLength());
} catch(NoSuchMethodException e) {
logger.error("Cannot create an instance of packet: constructor not found", e);
return;
} catch(InstantiationException e) {
logger.error("Cannot create an instance of packet", e);
return;
} catch(IllegalAccessException e) {
logger.error("Cannot create an instance of packet", e);
return;
} catch(InvocationTargetException e) {
logger.error("Cannot create an instance of packet", e);
return;
}
// The return values are always short, int or long.
// Here we don't have to mind about it as they are all serializable, and
// we can return an array of serializable-objects.
ArrayList retvals = new ArrayList();
Method[] allMethods = packetClass.getDeclaredMethods();
for(String fieldName : fieldsOrdered) {
boolean isArray = checkArray(fieldName);
for (Method method : allMethods) {
String methodName = method.getName();
try {
if(methodName.equals("get_" + fieldName)) {
method.setAccessible(true);
Serializable value = (Serializable) method.invoke(packetObject);
logger.debug("Using " + methodName);
logger.debug("Got: " + value.getClass().getCanonicalName());
if(isArray) {
for(int i=0; i < Array.getLength(value); i++) {
retvals.add((Serializable) Array.get(value, i));
}
} else {
retvals.add((Serializable) method.invoke(packetObject));
}
}
} catch (InvocationTargetException e) {
logger.error("Invocation of " + methodName + " failed: %s%n", e);
} catch (IllegalAccessException e) {
logger.error("Cannot access " + methodName, e);
}
}
}
postStreamElement( retvals.toArray(new Serializable[] {}) );
}
private boolean checkArray(String fieldName) {
String type = fields.get(fieldName);
if(type.contains("[]")) return true;
else return false;
}
/** Overrides ch.epfl.gsn.wrappers.AbstractWrapper.run
* Nothing to do, because the values are inserted to GSN whenever packets arrive.
*/
public void run ( ) {
}
public void dispose ( ) {
threadCounter--;
}
public final DataField [ ] getOutputFormat ( ) {
return outputStructureCache;
}
public String getWrapperName ( ) {
return "TinyOS packet wrapper";
}
/**
* Can be used by other classes which are extending this class to create elaborated communication such
* as send a packet back to the sensor network.
* @return
*/
protected MoteIF getMoteIF() {
return moteIF;
}
}