Commit 42bfa924 authored by David Johnson's avatar David Johnson

Most of the rest of the app code for logging packets. Still have to write

a bit more, then hack it into the testbed.
parent c94fe76c
import java.util.*;
//import net.tinyos.message.Message;
import net.tinyos.message.Message;
public class LogPacket {
......@@ -8,9 +8,12 @@ public class LogPacket {
private byte[] data;
private int packetType;
private int crc;
//private Message msg;
private Object msg;
private String srcVnodeName;
public LogPacket(Date time,byte[] data,int packetType,int crc) {
public LogPacket(String srcVnodeName,Date time,byte[] data,
int packetType,int crc) {
this.srcVnodeName = srcVnodeName;
this.timeStamp = time;
this.data = data;
this.packetType = packetType;
......@@ -34,4 +37,16 @@ public class LogPacket {
return this.crc;
}
public String getSrcVnodeName() {
return this.srcVnodeName;
}
public void setMsgObject(Object obj) {
this.msg = obj;
}
public Object getMsgObject() {
return this.msg;
}
}
......@@ -9,6 +9,7 @@ public class MoteLogger {
private int debug;
private File classDir;
private String[] classNames;
private File aclDir;
private String logTag;
private String pid;
......@@ -18,6 +19,7 @@ public class MoteLogger {
private String dbURL = "jdbc.DriverMysql";
private String dbUser = "root";
private String dbPass = "";
private Hashtable classes;
public static void main(String args[]) {
//
......@@ -35,6 +37,7 @@ public class MoteLogger {
public static void parseArgsAndRun(String args[]) {
File classDir = null;
String[] classNames = null;
String tag = null;
String pid = null;
String eid = null;
......@@ -44,12 +47,21 @@ public class MoteLogger {
int i;
for (i = 0; i < args.length; ++i) {
if (args[i].equals("-c")) {
if (args[i].equals("-C")) {
if (++i < args.length && !args[i].startsWith("-")) {
classDir = new File(args[i]);
}
else {
System.err.println("option '-c' must have an argument!");
System.err.println("option '-C' must have an argument!");
usage();
}
}
else if (args[i].equals("-c")) {
if (++i < args.length && !args[i].startsWith("-")) {
classNames = args[i].split(",");
}
else {
System.err.println("option -c must have an argument!");
usage();
}
}
......@@ -113,7 +125,7 @@ public class MoteLogger {
//this.motes = motes;
// startup
MoteLogger ml = new MoteLogger(classDir,aclDir,motes,
MoteLogger ml = new MoteLogger(classDir,classNames,aclDir,motes,
pid,eid,tag,debug);
ml.run();
}
......@@ -122,8 +134,10 @@ public class MoteLogger {
String usage = "" +
"Usage: java MoteLogger -cipMd \n" +
"Options:\n" +
"\t-c <classdir> Directory containing packet-matching " +
"\t-C <classdir> Directory containing packet-matching " +
"classfiles \n" +
"\t-c <classfile list> Comma-separated list of fully-qualified " +
"classnames. \n" +
"\t-i <idtag> Alphanumeric tag for this logging set \n" +
"\t-p pid,eid \n" +
//"\t-m <vname,vname,...> (list of vnames present in acl dir) \n"+
......@@ -135,15 +149,18 @@ public class MoteLogger {
System.exit(-1);
}
public MoteLogger(File classDir,File aclDir,String[] motes,String pid,
public MoteLogger(File classDir,String[] classNames,
File aclDir,String[] motes,String pid,
String eid,String tag,int debug) {
this.classDir = classDir;
this.classNames = classNames;
this.aclDir = aclDir;
this.pid = pid;
this.eid = eid;
this.logTag = tag;
this.debug = debug;
this.motes = motes;
this.classes = new Hashtable();
}
// I know, not a thread, but who cares
......@@ -177,6 +194,55 @@ public class MoteLogger {
System.exit(0);
}
// load classfiles and create Class objs so we can do instance objs
// on incoming packet data
// first, have to discover the classfiles, if they weren't specified.
if (this.classNames == null || classNames.length == 0) {
// try to read the classFile directory and use the XXX.class
// names as the classes -- will fail if classes are in package.
File[] classFiles = classDir.listFiles( new FilenameFilter() {
public boolean accept(File dir, String name) {
if (name != null && name.endsWith(".class")) {
return true;
}
else {
return false;
}
}
});
if (classFiles != null) {
this.classNames = new String[classFiles.length];
for (int i = 0; i < classFiles.length; ++i) {
String[] sa = classFiles[i].getName().split("\\.class");
this.classNames[i] = sa[0];
}
}
}
if (classFiles == null || classFiles.length == 0) {
System.out.println("Could not find any classfiles; exiting.");
System.exit(0);
}
// second, actually load them.
for (int i = 0; i < classNames.length; ++i) {
try {
Class c = Class.forName(classNames[i]);
// this call does the class.forName, redundant i know.
classes.put(c,new SQLGenerator(classNames[i],
null,
this.logTag));
}
catch (Exception e) {
System.err.println("Problem loading class " + classNames[i] +
":");
e.printStackTrace();
}
}
// get capture keys
Hashtable acls = new Hashtable();
for (int i = 0; i < motes.length; ++i) {
......@@ -194,22 +260,114 @@ public class MoteLogger {
packetQueue = new SynchQueue();
// connect to the database
;
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:" +
"//localhost/test?user=root");
}
catch (Exception e) {
System.err.println("FATAL -- couldn't connect to the database: " +
e.getMessage());
e.printStackTrace();
System.exit(-2);
}
// spawn connection threads
for (Enumeration e1 = acls.keys(); e1.hasMoreElements(); ) {
String vNN = (String)e1.nextElement();
ElabACL acl = (ElabACL)acls.get(vNN);
(new MoteLogThread(acl,packetQueue)).start();
(new MoteThread(acl,packetQueue)).start();
}
// now, process the packet queue forever.
while (true) {
LogPacket lp = null;
synchronized(packetQueue) {
while(packetQueue.peek() == null) {
try {
packetQueue.wait();
}
catch (Exception e) {
e.printStackTrace();
}
}
// once we get a packet...
lp = packetQueue.queueRemove();
}
// dump it out to database:
// this is the real work:
try {
// first match the packet:
SQLGenerator sq = null;
boolean foundMatch = false;
for (Enumeration e1 = classes.keys();
!foundMatch && e1.hasMoreElements(); ) {
Class cc = (Class)e1.nextElement();
sq = (SQLGenerator)classes.get(cc);
// Need to create a BaseTOSMsg
// and extract the 'type' field... which is the am type
// ... and then match this with the amType() method of the
// invoked class.
// XXX: note that this must be changed depending on the
// architecture of the motes we're connecting to, in order
// to decode using the correct host byte order assumption.
// i.e., net.tinyos.message.telos.*
//
if (true) {
net.tinyos.message.avrmote.BaseTOSMsg btm =
new net.tinyos.message.avrmote.BaseTOSMsg(lp.getData());
if (lp.amType() == sq.getAMType()) {
// class match
// invoke the byte[] data constructor on the data
// from the basetosmsg:
Constructor ccc = cc.getConstructor( new Class[] {
byte[].class } );
Object msgObj = ccc.newInstance( new Object[] {
btm.get_data() } );
lp.setMsgObject(msgObj);
// ready to log!!!
foundMatch = true;
}
}
}
// do the db insert:
sq.storeMessage(msgObj,conn);
// that's all, folks!
}
catch (Exception e) {
System.err.println("Error while logging packet: ");
e.printStackTrace();
}
}
}
class MoteLogThread extends Thread {
class MoteThread extends Thread {
private String vNodeName;
private ElabACL acl;
private SynchQueue q;
private Socket sock;
public MoteLogThread(ElabACL acl,SynchQueue packetQueue) {
public MoteLogThread(String vNodeName,ElabACL acl,
SynchQueue packetQueue) {
this.vNodeName = vNodeName;
this.acl = acl;
this.q = packetQueue;
}
......@@ -313,7 +471,7 @@ public class MoteLogger {
PacketReader pr = null;
try {
pr = new PacketReader(sock.getInputStream());
pr = new PacketReader(vNodeName,sock.getInputStream());
}
catch (Exception e) {
e.printStackTrace();
......@@ -324,6 +482,11 @@ public class MoteLogger {
lp = null;
try {
lp = pr.readPacket();
synchronized(packetQueue) {
packetQueue.queueAdd(lp);
packetQueue.notifyAll();
}
}
catch (Exception e) {
System.err.println("Problem while reading from " +
......
......@@ -64,8 +64,10 @@ public final class PacketReader {
final static int P_UNKNOWN = 255;
private InputStream in;
private String vNodeName;
public PacketReader(InputStream in) {
public PacketReader(String vNodeName,InputStream in) {
this.vNodeName = vNodeName;
this.in = in;
}
......@@ -148,13 +150,13 @@ public final class PacketReader {
retval = new byte[buf.length-3];
System.arraycopy(buf,1,retval,0,retval.length);
return new LogPacket(t,retval,packetType,crc);
return new LogPacket(vNodeName,t,retval,packetType,crc);
}
else if (buf[0] == P_PACKET_ACK) {
retval = new byte[buf.length-4];
System.arraycopy(buf,2,retval,0,retval.length);
return new LogPacket(t,retval,packetType,crc);
return new LogPacket(vNodeName,t,retval,packetType,crc);
}
else if (buf[0] == P_ACK || buf[0] == P_UNKNOWN) {
// do nothing for now; this is only sent by receiver on
......@@ -165,7 +167,7 @@ public final class PacketReader {
// XXX: might want to log these in the future...
}
return lp;
return null;
}
}
......
import net.tinyos.message.*;
import java.sql.*;
import java.util.*;
import java.lang.reflect.*;
public class SQLGenerator {
//private Message m;
// every message should have these fields...
private Method[] tosMsgFields;
// for now, even structures will be completely flattened out
private Method[] simpleFields;
// arrays are the only thing we will distinguish...
// and heaven help us if there's a multidimensional one!
// there could be more than one, hence the hash
// keys:
//private Hashtable arrayFields;
private Method[] arrayFields;
// table creation statements
private String[] tableCreates;
// view creates... reduces number of tables and data dup.
private String[] viewCreates;
// data insert preparedStatement strings
private String[] inserts;
// first dimension i is how to perform inserts[i];
// each element in the arrayList is a String[] with the following data:
// [ getMethodName, returnType, SQLType ]
private ArrayList[] insertInfo;
private String className;
private String msgSpec;
private String tag;
private int amType;
private boolean tablesCreated;
public SQLGenerator(String name,String[] spec,String tag)
throws ClassNotFoundException, Exception {
// need to parse the methods via reflection:
this.className = name;
this.msgSpec = spec;
this.tag = tag;
Class c = Class.forName(className);
// grab the am type first!
Object msgObj = c.newInstance();
Method am = c.getMethod("amType",null);
Integer amt = (Integer)am.invoke(msgObj,null);
this.amType = amt.intValue();
// now figure out the methods. Basically, we figure out which
// get_* methods correspond to which arrays by finding the longest
// common substring following the get_ part of the string.
Methods[] m = c.getMethods();
Vector flatFieldMethods = new Vector();
Vector arrayFieldMethods = new Vector();
for (int i = 0; i < m.length; ++i) {
String ms = m[i].getName();
String rts = m[i].getReturnType().getName();
if (ms.startsWith("get_")) {
if (rts.startsWith("[[")) {
// MULTIDIMENSIONAL ARRAYS NOT SUPPORTED YET
throw new Exception("multidimensional arrays unsupported");
}
else if (rts.startsWith("[")) {
arrayFieldMethods.add(m[i]);
}
else {
flatFieldMethods.add(m[i]);
}
}
}
simpleFields = new Method[flatFieldMethods.size()];
arrayFields = new Method[arrayFieldMethods.size()];
int elm = 0;
for (Enumeration e1 = flatFieldMethods.elements();
e1.hasMoreElements(); ) {
simpleFields[elm++] = (Method)e1.nextElement();
}
elm = 0;
for (Enumeration e1 = arrayFieldMethods.elements();
e1.hasMoreElements(); ) {
arrayFields[elm++] = (Method)e1.nextElement();
}
// so for now, we're just going to have to accept the reality of
// flattened everything. The best we can do is create the main table,
// with blobs for array data, and a secondary table, in which we change
// the blobs to string representation of the array.
// not true -- by asking for a bit more MIG output, we can do
// infinitely better -- because the whole type definition is right
// there, along with variable names. Just have to read the MIG src,
// write a parser... then generate much, much more complex sql.
// for now, just a few tables -- a main data table,
// then several tables for array info.
if (spec != null) {
// do the complex stuff...
;
}
else {
// simple, one-table layout with two views.
int numTables = 1 + arrayFields.length;
int tableIndex = 1;
tableCreates = new String[numTables];
inserts = new String[numTables];
insertInfo = new ArrayList[numTables];
viewCreates = new String[1];
// main table
String tableName = className + "__data_" + tag;
parentTableName = tableName;
tableCreates[0] = "" +
"CREATE TABLE " + tableName + " " +
"(" + "id INT PRIMARY KEY AUTO_INCREMENT, " +
"time DATETIME, " +
"amType INT, " +
"srcMote VARCHAR(32)";
inserts[0] = "insert into " + tableName +
" values (NULL,?," + this.amType + ",?";
insertInfo[0] = new ArrayList();
insertInfo[0].add( new String[] { "date",null,null } );
// now get this statically.
//insertInfo[0].add( new String[] { "amType","I","INT" } );
insertInfo[0].add( new String[] { "srcMote",null,null } );
// now the msg-specific stuff:
// we do this again here because we'd prefer the correct order...
Method ma[] = c.getMethods();
for (int i = 0; i < ma.length; ++i) {
String mn = ma[i].getName();
if (mn.startsWith("get_")) {
String mnSansGet = mn.subString(4,mnSansGet.length());
Method signMethod = null;
try {
signMethod = c.getMethod("isSigned_" + mnSansGet);
}
catch (Exception e) {
// no big deal
;
}
boolean isArr = false;
String internalArraySQLType = "";
if (ma[i].getReturnType().getName().startsWith("[")) {
isArr = true;
internalArraySQLType =
getSQLTypeStr(ma[i],signMethod,true);
}
if (!isArr) {
tableCreates[0] += ", " + mnSansGet + " " +
getSQLTypeStr(ma[i],signMethod,false);
// add to the insert & insertInfo
inserts[0] += ",?";
insertInfo[0].add( new String[] {
ma[i].getName(),
ma[i].getReturnType().getName(),
getSQLTypeStr(ma[i],signMethod,false) } );
}
else {
// now, IF we have an array, we create a separate
// table. we only do this for arrays! We let the
// structs get flattened out for now; we solve the
// hierarchy problem as best we can via views.
// we add the table now:
String aTableName = mnSansGet + "__data_" + tag;
tableCreates[tableIdx] = "" +
"CREATE TABLE " + aTableName + " (" +
"ppid INT PRIMARY KEY NOT NULL, " +
"idx INT NOT NULL, " +
"" + mnSansGet + " " + internalArraySQLType +
")";
inserts[tableIdx] = "" +
"insert into " + aTableName + "values (" +
"?,?,?)";
insertInfo[tableIdx] = new ArrayList();
// [ getMethodName, returnType, SQLType ]
insertInfo[tableIdx].add( new String[] {
"ppid",null,null } );
insertInfo[tableIdx].add( new String[] {
"idx",null,null } );
insertInfo[tableIdx].add( new String[] {
ma[i].getName(),
ma[i].getReturnType().getName(),
internalArraySQLType } );
++tableIdx;
// also add the field as a blob column, just in case
tableCreates[0] += ", " + mnSansGet + " " +
getSQLTypeStr(ma[i],signMethod,false);
;
}
}
}
// finish off the table create and insert:
tableCreates[0] += ")";
inserts[0] += ")";
}
tablesCreated = false;
}
public String[] getTableCreates() {
return tableCreates;
}
public String[] getViewCreates() {
return viewCreates;
}
//
public boolean storeMessage(LogPacket lp,Connection conn)
throws SQLException {
// check if the tables are there; if not, create them.
if (!tablesCreated) {
try {
Statement s = conn.createStatement();
for (int i = 0; i < tableCreates.length; ++i) {
s.execute(tableCreates[i]);
}
tablesCreated = true;
}
catch (SQLException e) {
System.err.println("Problem while creating tables for " +
this.className + "!");
e.printStackTrace();
}
}
// run the inserts:
int errors = 0;
for (int i = 0; i < inserts.length; ++i) {
ArrayList aInfo = insertInfo[i];
// setup the prepared stmt:
try {
PreparedStatement ps = conn.prepareStatement(inserts[i]);
int ppid = -1;
for (int j = 0; j < aInfo.length(); ++j) {
String[] info = (String[])aInfo.get(j);
// now perform the ps.set op...
if (info[0].equals("date")) {
// insert the date manually
// note that we use j+1 since preparedStatement vars
// start at index 1
ps.setDate(j+1,lp.getDate());
}
else if (info[0].equals("srcMote")) {
ps.setString(j+1,lp.getSrcMote());
}
else if (info[0].equals("ppid")) {
ps.setInt(j+1,ppid);
}
else if (info[0].equals("idx")) {
;
}
else {