All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

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();
}
class MoteLogThread extends Thread {
// 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 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")) {