From e317eb26a483de73fd5af581b0916d6b2835a250 Mon Sep 17 00:00:00 2001 From: klueska Date: Tue, 17 Jun 2008 21:10:35 +0000 Subject: [PATCH] added java stuff to the TestCollection apps --- .../TestCollection/java/ColorCellEditor.java | 55 ++++ .../apps/TestCollection/java/Data.java | 74 +++++ .../apps/TestCollection/java/Graph.java | 232 ++++++++++++++ .../apps/TestCollection/java/Makefile | 21 ++ .../apps/TestCollection/java/Node.java | 101 ++++++ .../TestCollection/java/Oscilloscope.java | 128 ++++++++ .../apps/TestCollection/java/Window.java | 294 ++++++++++++++++++ .../apps/TestCollection/java/build.xml | 19 ++ .../apps/TestCollection/java/oscilloscope.jar | Bin 0 -> 19256 bytes apps/tosthreads/apps/TestCollection/java/run | 7 + .../TestCollection/java/ColorCellEditor.java | 55 ++++ .../capps/TestCollection/java/Data.java | 74 +++++ .../capps/TestCollection/java/Graph.java | 232 ++++++++++++++ .../capps/TestCollection/java/Makefile | 21 ++ .../capps/TestCollection/java/Node.java | 101 ++++++ .../TestCollection/java/Oscilloscope.java | 128 ++++++++ .../capps/TestCollection/java/Window.java | 294 ++++++++++++++++++ .../capps/TestCollection/java/build.xml | 19 ++ .../TestCollection/java/oscilloscope.jar | Bin 0 -> 19256 bytes apps/tosthreads/capps/TestCollection/java/run | 7 + 20 files changed, 1862 insertions(+) create mode 100644 apps/tosthreads/apps/TestCollection/java/ColorCellEditor.java create mode 100644 apps/tosthreads/apps/TestCollection/java/Data.java create mode 100644 apps/tosthreads/apps/TestCollection/java/Graph.java create mode 100644 apps/tosthreads/apps/TestCollection/java/Makefile create mode 100644 apps/tosthreads/apps/TestCollection/java/Node.java create mode 100644 apps/tosthreads/apps/TestCollection/java/Oscilloscope.java create mode 100644 apps/tosthreads/apps/TestCollection/java/Window.java create mode 100644 apps/tosthreads/apps/TestCollection/java/build.xml create mode 100644 apps/tosthreads/apps/TestCollection/java/oscilloscope.jar create mode 100755 apps/tosthreads/apps/TestCollection/java/run create mode 100644 apps/tosthreads/capps/TestCollection/java/ColorCellEditor.java create mode 100644 apps/tosthreads/capps/TestCollection/java/Data.java create mode 100644 apps/tosthreads/capps/TestCollection/java/Graph.java create mode 100644 apps/tosthreads/capps/TestCollection/java/Makefile create mode 100644 apps/tosthreads/capps/TestCollection/java/Node.java create mode 100644 apps/tosthreads/capps/TestCollection/java/Oscilloscope.java create mode 100644 apps/tosthreads/capps/TestCollection/java/Window.java create mode 100644 apps/tosthreads/capps/TestCollection/java/build.xml create mode 100644 apps/tosthreads/capps/TestCollection/java/oscilloscope.jar create mode 100755 apps/tosthreads/capps/TestCollection/java/run diff --git a/apps/tosthreads/apps/TestCollection/java/ColorCellEditor.java b/apps/tosthreads/apps/TestCollection/java/ColorCellEditor.java new file mode 100644 index 00000000..f88b0b6f --- /dev/null +++ b/apps/tosthreads/apps/TestCollection/java/ColorCellEditor.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006 Intel Corporation + * All rights reserved. + * + * This file is distributed under the terms in the attached INTEL-LICENSE + * file. If you do not find these files, copies can be found by writing to + * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, + * 94704. Attention: Intel License Inquiry. + */ + +import javax.swing.*; +import javax.swing.table.*; +import java.awt.*; +import java.awt.event.*; + +/* Editor for table cells representing colors. Popup a color chooser. */ +public class ColorCellEditor extends AbstractCellEditor + implements TableCellEditor { + private Color color; + private JButton button; + + public ColorCellEditor(String title) { + button = new JButton(); + final JColorChooser chooser = new JColorChooser(); + final JDialog dialog = JColorChooser.createDialog + (button, title, true, chooser, + new ActionListener() { + public void actionPerformed(ActionEvent e) { + color = chooser.getColor(); + } }, + null); + + button.setBorderPainted(false); + button.addActionListener + (new ActionListener () { + public void actionPerformed(ActionEvent e) { + button.setBackground(color); + chooser.setColor(color); + dialog.setVisible(true); + fireEditingStopped(); + } } ); + + } + + public Object getCellEditorValue() { return color; } + public Component getTableCellEditorComponent(JTable table, + Object value, + boolean isSelected, + int row, + int column) { + color = (Color)value; + return button; + } +} + diff --git a/apps/tosthreads/apps/TestCollection/java/Data.java b/apps/tosthreads/apps/TestCollection/java/Data.java new file mode 100644 index 00000000..ac35aa72 --- /dev/null +++ b/apps/tosthreads/apps/TestCollection/java/Data.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2006 Intel Corporation + * All rights reserved. + * + * This file is distributed under the terms in the attached INTEL-LICENSE + * file. If you do not find these files, copies can be found by writing to + * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, + * 94704. Attention: Intel License Inquiry. + */ + +import java.util.*; + +/* Hold all data received from motes */ +class Data { + /* The mote data is stored in a flat array indexed by a mote's identifier. + A null value indicates no mote with that identifier. */ + private Node[] nodes = new Node[256]; + private Oscilloscope parent; + + Data(Oscilloscope parent) { + this.parent = parent; + } + + /* Data received from mote nodeId containing NREADINGS samples from + messageId * NREADINGS onwards. Tell parent if this is a new node. */ + void update(int nodeId, int messageId, int readings[]) { + if (nodeId >= nodes.length) { + int newLength = nodes.length * 2; + if (nodeId >= newLength) + newLength = nodeId + 1; + + Node newNodes[] = new Node[newLength]; + System.arraycopy(nodes, 0, newNodes, 0, nodes.length); + nodes = newNodes; + } + Node node = nodes[nodeId]; + if (node == null) { + nodes[nodeId] = node = new Node(nodeId); + parent.newNode(nodeId); + } + node.update(messageId, readings); + } + + /* Return value of sample x for mote nodeId, or -1 for missing data */ + int getData(int nodeId, int x) { + if (nodeId >= nodes.length || nodes[nodeId] == null) + return -1; + return nodes[nodeId].getData(x); + } + + /* Return number of last known sample on mote nodeId. Returns 0 for + unknown motes. */ + int maxX(int nodeId) { + if (nodeId >= nodes.length || nodes[nodeId] == null) + return 0; + return nodes[nodeId].maxX(); + } + + /* Return number of largest known sample on all motes (0 if there are no + motes) */ + int maxX() { + int max = 0; + + for (int i = 0; i < nodes.length; i++) + if (nodes[i] != null) { + int nmax = nodes[i].maxX(); + + if (nmax > max) + max = nmax; + } + + return max; + } +} diff --git a/apps/tosthreads/apps/TestCollection/java/Graph.java b/apps/tosthreads/apps/TestCollection/java/Graph.java new file mode 100644 index 00000000..9a42c1c8 --- /dev/null +++ b/apps/tosthreads/apps/TestCollection/java/Graph.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2006 Intel Corporation + * All rights reserved. + * + * This file is distributed under the terms in the attached INTEL-LICENSE + * file. If you do not find these files, copies can be found by writing to + * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, + * 94704. Attention: Intel License Inquiry. + */ + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.util.*; + +/* Panel for drawing mote-data graphs */ +class Graph extends JPanel +{ + final static int BORDER_LEFT = 40; + final static int BORDER_RIGHT = 0; + final static int BORDER_TOP = 10; + final static int BORDER_BOTTOM = 10; + + final static int TICK_SPACING = 40; + final static int MAX_TICKS = 16; + final static int TICK_WIDTH = 10; + + final static int MIN_WIDTH = 50; + + int gx0, gx1, gy0, gy1; // graph bounds + int scale = 2; // gx1 - gx0 == MIN_WIDTH << scale + Window parent; + + /* Graph to screen coordinate conversion support */ + int height, width; + double xscale, yscale; + + void updateConversion() { + height = getHeight() - BORDER_TOP - BORDER_BOTTOM; + width = getWidth() - BORDER_LEFT - BORDER_RIGHT; + if (height < 1) + height = 1; + if (width < 1) + width = 1; + xscale = (double)width / (gx1 - gx0 + 1); + yscale = (double)height / (gy1 - gy0 + 1); + } + + Graphics makeClip(Graphics g) { + return g.create(BORDER_LEFT, BORDER_TOP, width, height); + } + + // Note that these do not include the border offset! + int screenX(int gx) { + return (int)(xscale * (gx - gx0) + 0.5); + } + + int screenY(int gy) { + return (int)(height - yscale * (gy - gy0)); + } + + int graphX(int sx) { + return (int)(sx / xscale + gx0 + 0.5); + } + + Graph(Window parent) { + this.parent = parent; + gy0 = 0; gy1 = 0xffff; + gx0 = 0; gx1 = MIN_WIDTH << scale; + } + + void rightDrawString(Graphics2D g, String s, int x, int y) { + TextLayout layout = + new TextLayout(s, parent.smallFont, g.getFontRenderContext()); + Rectangle2D bounds = layout.getBounds(); + layout.draw(g, x - (float)bounds.getWidth(), y + (float)bounds.getHeight() / 2); + } + + protected void paintComponent(Graphics g) { + Graphics2D g2d = (Graphics2D)g; + + /* Repaint. Synchronize on Oscilloscope to avoid data changing. + Simply clear panel, draw Y axis and all the mote graphs. */ + synchronized (parent.parent) { + updateConversion(); + g2d.setColor(Color.BLACK); + g2d.fillRect(0, 0, getWidth(), getHeight()); + drawYAxis(g2d); + + Graphics clipped = makeClip(g2d); + int count = parent.moteListModel.size(); + for (int i = 0; i < count; i++) { + clipped.setColor(parent.moteListModel.getColor(i)); + drawGraph(clipped, parent.moteListModel.get(i)); + } + } + } + + /* Draw the Y-axis */ + protected void drawYAxis(Graphics2D g) { + int axis_x = BORDER_LEFT - 1; + int height = getHeight() - BORDER_BOTTOM - BORDER_TOP; + + g.setColor(Color.WHITE); + g.drawLine(axis_x, BORDER_TOP, axis_x, BORDER_TOP + height - 1); + + /* Draw a reasonable set of tick marks */ + int nTicks = height / TICK_SPACING; + if (nTicks > MAX_TICKS) + nTicks = MAX_TICKS; + + int tickInterval = (gy1 - gy0 + 1) / nTicks; + if (tickInterval == 0) + tickInterval = 1; + + /* Tick interval should be of the family A * 10^B, + where A = 1, 2 * or 5. We tend more to rounding A up, to reduce + rather than increase the number of ticks. */ + int B = (int)(Math.log(tickInterval) / Math.log(10)); + int A = (int)(tickInterval / Math.pow(10, B) + 0.5); + if (A > 2) A = 5; + else if (A > 5) A = 10; + + tickInterval = A * (int)Math.pow(10, B); + + /* Ticks are printed at multiples of tickInterval */ + int tick = ((gy0 + tickInterval - 1) / tickInterval) * tickInterval; + while (tick <= gy1) { + int stick = screenY(tick) + BORDER_TOP; + rightDrawString(g, "" + tick, axis_x - TICK_WIDTH / 2 - 2, stick); + g.drawLine(axis_x - TICK_WIDTH / 2, stick, + axis_x - TICK_WIDTH / 2 + TICK_WIDTH, stick); + tick += tickInterval; + } + + } + + /* Draw graph for mote nodeId */ + protected void drawGraph(Graphics g, int nodeId) { + SingleGraph sg = new SingleGraph(g, nodeId); + + if (gx1 - gx0 >= width) // More points than pixels-iterate by pixel + for (int sx = 0; sx < width; sx++) + sg.nextPoint(g, graphX(sx), sx); + else // Less points than pixel-iterate by points + for (int gx = gx0; gx <= gx1; gx++) + sg.nextPoint(g, gx, screenX(gx)); + } + + /* Inner class to simplify drawing a graph. Simplify initialise it, then + feed it the X screen and graph coordinates, from left to right. */ + private class SingleGraph { + int lastsx, lastsy, nodeId; + + /* Start drawing the graph mote id */ + SingleGraph(Graphics g, int id) { + nodeId = id; + lastsx = -1; + lastsy = -1; + } + + /* Next point in mote's graph is at x value gx, screen coordinate sx */ + void nextPoint(Graphics g, int gx, int sx) { + int gy = parent.parent.data.getData(nodeId, gx); + int sy = -1; + + if (gy >= 0) { // Ignore missing values + double rsy = height - yscale * (gy - gy0); + + // Ignore problem values + if (rsy >= -1e6 && rsy <= 1e6) + sy = (int)(rsy + 0.5); + + if (lastsy >= 0 && sy >= 0) + g.drawLine(lastsx, lastsy, sx, sy); + } + lastsx = sx; + lastsy = sy; + } + } + + /* Update X-axis range in GUI */ + void updateXLabel() { + parent.xLabel.setText("X: " + gx0 + " - " + gx1); + } + + /* Ensure that graph is nicely positioned on screen. max is the largest + sample number received from any mote. */ + private void recenter(int max) { + // New data will show up at the 3/4 point + // The 2nd term ensures that gx1 will be >= max + int scrollby = ((gx1 - gx0) >> 2) + (max - gx1); + gx0 += scrollby; + gx1 += scrollby; + if (gx0 < 0) { // don't bother showing negative sample numbers + gx1 -= gx0; + gx0 = 0; + } + updateXLabel(); + } + + /* New data received. Redraw graph, scrolling if necessary */ + void newData() { + int max = parent.parent.data.maxX(); + + if (max > gx1 || max < gx0) // time to scroll + recenter(max); + repaint(); + } + + /* User set the X-axis scale to newScale */ + void setScale(int newScale) { + gx1 = gx0 + (MIN_WIDTH << newScale); + scale = newScale; + recenter(parent.parent.data.maxX()); + repaint(); + } + + /* User attempted to set Y-axis range to newy0..newy1. Refuse bogus + values (return false), or accept, redraw and return true. */ + boolean setYAxis(int newy0, int newy1) { + if (newy0 >= newy1 || newy0 < 0 || newy0 > 65535 || + newy1 < 0 || newy1 > 65535) + return false; + gy0 = newy0; + gy1 = newy1; + repaint(); + return true; + } +} diff --git a/apps/tosthreads/apps/TestCollection/java/Makefile b/apps/tosthreads/apps/TestCollection/java/Makefile new file mode 100644 index 00000000..55c2605b --- /dev/null +++ b/apps/tosthreads/apps/TestCollection/java/Makefile @@ -0,0 +1,21 @@ +GEN=OscilloscopeMsg.java Constants.java + +all: oscilloscope.jar + +oscilloscope.jar: Oscilloscope.class + jar cf $@ *.class + +OscilloscopeMsg.java: ../MultihopOscilloscope.h + mig -target=null -java-classname=OscilloscopeMsg java ../MultihopOscilloscope.h oscilloscope -o $@ + +Constants.java: ../MultihopOscilloscope.h + ncg -target=null -java-classname=Constants java ../MultihopOscilloscope.h NREADINGS DEFAULT_INTERVAL -o $@ + +Oscilloscope.class: $(wildcard *.java) $(GEN) + javac *.java + +clean: + rm -f *.class $(GEN) + +veryclean: clean + rm oscilloscope.jar diff --git a/apps/tosthreads/apps/TestCollection/java/Node.java b/apps/tosthreads/apps/TestCollection/java/Node.java new file mode 100644 index 00000000..cfe8db9e --- /dev/null +++ b/apps/tosthreads/apps/TestCollection/java/Node.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2006 Intel Corporation + * All rights reserved. + * + * This file is distributed under the terms in the attached INTEL-LICENSE + * file. If you do not find these files, copies can be found by writing to + * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, + * 94704. Attention: Intel License Inquiry. + */ + +/** + * Class holding all data received from a mote. + */ +class Node { + /* Data is hold in an array whose size is a multiple of INCREMENT, and + INCREMENT itself must be a multiple of Constant.NREADINGS. This + simplifies handling the extension and clipping of old data + (see setEnd) */ + final static int INCREMENT = 100 * Constants.NREADINGS; + final static int MAX_SIZE = 100 * INCREMENT; // Must be multiple of INCREMENT + + /* The mote's identifier */ + int id; + + /* Data received from the mote. data[0] is the dataStart'th sample + Indexes 0 through dataEnd - dataStart - 1 hold data. + Samples are 16-bit unsigned numbers, -1 indicates missing data. */ + int[] data; + int dataStart, dataEnd; + + Node(int _id) { + id = _id; + } + + /* Update data to hold received samples newDataIndex .. newEnd. + If we receive data with a lower index, we discard newer data + (we assume the mote rebooted). */ + private void setEnd(int newDataIndex, int newEnd) { + if (newDataIndex < dataStart || data == null) { + /* New data is before the start of what we have. Just throw it + all away and start again */ + dataStart = newDataIndex; + data = new int[INCREMENT]; + } + if (newEnd > dataStart + data.length) { + /* Try extending first */ + if (data.length < MAX_SIZE) { + int newLength = (newEnd - dataStart + INCREMENT - 1) / INCREMENT * INCREMENT; + if (newLength >= MAX_SIZE) + newLength = MAX_SIZE; + + int[] newData = new int[newLength]; + System.arraycopy(data, 0, newData, 0, data.length); + data = newData; + + } + if (newEnd > dataStart + data.length) { + /* Still doesn't fit. Squish. + We assume INCREMENT >= (newEnd - newDataIndex), and ensure + that dataStart + data.length - INCREMENT = newDataIndex */ + int newStart = newDataIndex + INCREMENT - data.length; + + if (dataStart + data.length > newStart) + System.arraycopy(data, newStart - dataStart, data, 0, + data.length - (newStart - dataStart)); + dataStart = newStart; + } + } + /* Mark any missing data as invalid */ + for (int i = dataEnd < dataStart ? dataStart : dataEnd; + i < newDataIndex; i++) + data[i - dataStart] = -1; + + /* If we receive a count less than the old count, we assume the old + data is invalid */ + dataEnd = newEnd; + + } + + /* Data received containing NREADINGS samples from messageId * NREADINGS + onwards */ + void update(int messageId, int readings[]) { + int start = messageId * Constants.NREADINGS; + setEnd(start, start + Constants.NREADINGS); + for (int i = 0; i < readings.length; i++) + data[start - dataStart + i] = readings[i]; + } + + /* Return value of sample x, or -1 for missing data */ + int getData(int x) { + if (x < dataStart || x >= dataEnd) + return -1; + else + return data[x - dataStart]; + } + + /* Return number of last known sample */ + int maxX() { + return dataEnd - 1; + } +} diff --git a/apps/tosthreads/apps/TestCollection/java/Oscilloscope.java b/apps/tosthreads/apps/TestCollection/java/Oscilloscope.java new file mode 100644 index 00000000..3db3741f --- /dev/null +++ b/apps/tosthreads/apps/TestCollection/java/Oscilloscope.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2006 Intel Corporation + * All rights reserved. + * + * This file is distributed under the terms in the attached INTEL-LICENSE + * file. If you do not find these files, copies can be found by writing to + * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, + * 94704. Attention: Intel License Inquiry. + */ + +import net.tinyos.message.*; +import net.tinyos.util.*; +import java.io.*; + +/* The "Oscilloscope" demo app. Displays graphs showing data received from + the Oscilloscope mote application, and allows the user to: + - zoom in or out on the X axis + - set the scale on the Y axis + - change the sampling period + - change the color of each mote's graph + - clear all data + + This application is in three parts: + - the Node and Data objects store data received from the motes and support + simple queries + - the Window and Graph and miscellaneous support objects implement the + GUI and graph drawing + - the Oscilloscope object talks to the motes and coordinates the other + objects + + Synchronization is handled through the Oscilloscope object. Any operation + that reads or writes the mote data must be synchronized on Oscilloscope. + Note that the messageReceived method below is synchronized, so no further + synchronization is needed when updating state based on received messages. +*/ +public class Oscilloscope implements MessageListener +{ + MoteIF mote; + Data data; + Window window; + + /* The current sampling period. If we receive a message from a mote + with a newer version, we update our interval. If we receive a message + with an older version, we broadcast a message with the current interval + and version. If the user changes the interval, we increment the + version and broadcast the new interval and version. */ + int interval = Constants.DEFAULT_INTERVAL; + int version = -1; + + /* Main entry point */ + void run() { + data = new Data(this); + window = new Window(this); + window.setup(); + mote = new MoteIF(PrintStreamMessenger.err); + mote.registerListener(new OscilloscopeMsg(), this); + } + + /* The data object has informed us that nodeId is a previously unknown + mote. Update the GUI. */ + void newNode(int nodeId) { + window.newNode(nodeId); + } + + synchronized public void messageReceived(int dest_addr, Message msg) { + if (msg instanceof OscilloscopeMsg) { + OscilloscopeMsg omsg = (OscilloscopeMsg)msg; + + /* Update interval and mote data */ + periodUpdate(omsg.get_version(), omsg.get_interval()); + data.update(omsg.get_id(), omsg.get_count(), omsg.get_readings()); + + /* Inform the GUI that new data showed up */ + window.newData(); + } + } + + /* A potentially new version and interval has been received from the + mote */ + void periodUpdate(int moteVersion, int moteInterval) { + if (moteVersion > version) { + /* It's new. Update our vision of the interval. */ + version = moteVersion; + interval = moteInterval; + window.updateSamplePeriod(); + } + else if (moteVersion < version) { + /* It's old. Update the mote's vision of the interval. */ + sendInterval(); + } + } + + /* The user wants to set the interval to newPeriod. Refuse bogus values + and return false, or accept the change, broadcast it, and return + true */ + synchronized boolean setInterval(int newPeriod) { + if (newPeriod < 1 || newPeriod > 65535) + return false; + interval = newPeriod; + version++; + sendInterval(); + return true; + } + + /* Broadcast a version+interval message. */ + void sendInterval() { + OscilloscopeMsg omsg = new OscilloscopeMsg(); + + omsg.set_version(version); + omsg.set_interval(interval); + try { + mote.send(MoteIF.TOS_BCAST_ADDR, omsg); + } + catch (IOException e) { + window.error("Cannot send message to mote"); + } + } + + /* User wants to clear all data. */ + void clear() { + data = new Data(this); + } + + public static void main(String[] args) { + Oscilloscope me = new Oscilloscope(); + me.run(); + } +} diff --git a/apps/tosthreads/apps/TestCollection/java/Window.java b/apps/tosthreads/apps/TestCollection/java/Window.java new file mode 100644 index 00000000..d7979bf9 --- /dev/null +++ b/apps/tosthreads/apps/TestCollection/java/Window.java @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2006 Intel Corporation + * All rights reserved. + * + * This file is distributed under the terms in the attached INTEL-LICENSE + * file. If you do not find these files, copies can be found by writing to + * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, + * 94704. Attention: Intel License Inquiry. + */ + +import javax.swing.*; +import javax.swing.table.*; +import javax.swing.event.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +/* The main GUI object. Build the GUI and coordinate all user activities */ +class Window +{ + Oscilloscope parent; + Graph graph; + + Font smallFont = new Font("Dialog", Font.PLAIN, 8); + Font boldFont = new Font("Dialog", Font.BOLD, 12); + Font normalFont = new Font("Dialog", Font.PLAIN, 12); + MoteTableModel moteListModel; // GUI view of mote list + JLabel xLabel; // Label displaying X axis range + JTextField sampleText, yText; // inputs for sample period and Y axis range + JFrame frame; + + Window(Oscilloscope parent) { + this.parent = parent; + } + + /* A model for the mote table, and general utility operations on the mote + list */ + class MoteTableModel extends AbstractTableModel { + private ArrayList motes = new ArrayList(); + private ArrayList colors = new ArrayList(); + + /* Initial mote colors cycle through this list. Add more colors if + you want. */ + private Color[] cycle = { + Color.RED, Color.WHITE, Color.GREEN, Color.MAGENTA, + Color.YELLOW, Color.GRAY, Color.YELLOW + }; + int cycleIndex; + + /* TableModel methods for achieving our table appearance */ + public String getColumnName(int col) { + if (col == 0) + return "Mote"; + else + return "Color"; + } + public int getColumnCount() { return 2; } + public synchronized int getRowCount() { return motes.size(); } + public synchronized Object getValueAt(int row, int col) { + if (col == 0) + return motes.get(row); + else + return colors.get(row); + } + public Class getColumnClass(int col) { + return getValueAt(0, col).getClass(); + } + public boolean isCellEditable(int row, int col) { return col == 1; } + public synchronized void setValueAt(Object value, int row, int col) { + colors.set(row, value); + fireTableCellUpdated(row, col); + graph.repaint(); + } + + /* Return mote id of i'th mote */ + int get(int i) { return ((Integer)motes.get(i)).intValue(); } + + /* Return color of i'th mote */ + Color getColor(int i) { return (Color)colors.get(i); } + + /* Return number of motes */ + int size() { return motes.size(); } + + /* Add a new mote */ + synchronized void newNode(int nodeId) { + /* Shock, horror. No binary search. */ + int i, len = motes.size(); + + for (i = 0; ; i++) + if (i == len || nodeId < get(i)) { + motes.add(i, new Integer(nodeId)); + // Cycle through a set of initial colors + colors.add(i, cycle[cycleIndex++ % cycle.length]); + break; + } + fireTableRowsInserted(i, i); + } + + /* Remove all motes */ + void clear() { + motes = new ArrayList(); + colors = new ArrayList(); + fireTableDataChanged(); + } + } + + /* A simple full-color cell */ + static class MoteColor extends JLabel implements TableCellRenderer { + public MoteColor() { setOpaque(true); } + public Component getTableCellRendererComponent + (JTable table, Object color, + boolean isSelected, boolean hasFocus, int row, int column) { + setBackground((Color)color); + return this; + } + } + + /* Convenience methods for making buttons, labels and textfields. + Simplifies code and ensures a consistent style. */ + + JButton makeButton(String label, ActionListener action) { + JButton button = new JButton(); + button.setText(label); + button.setFont(boldFont); + button.addActionListener(action); + return button; + } + + JLabel makeLabel(String txt, int alignment) { + JLabel label = new JLabel(txt, alignment); + label.setFont(boldFont); + return label; + } + + JLabel makeSmallLabel(String txt, int alignment) { + JLabel label = new JLabel(txt, alignment); + label.setFont(smallFont); + return label; + } + + JTextField makeTextField(int columns, ActionListener action) { + JTextField tf = new JTextField(columns); + tf.setFont(normalFont); + tf.setMaximumSize(tf.getPreferredSize()); + tf.addActionListener(action); + return tf; + } + + /* Build the GUI */ + void setup() { + JPanel main = new JPanel(new BorderLayout()); + + main.setMinimumSize(new Dimension(500, 250)); + main.setPreferredSize(new Dimension(800, 400)); + + // Three panels: mote list, graph, controls + moteListModel = new MoteTableModel(); + JTable moteList = new JTable(moteListModel); + moteList.setDefaultRenderer(Color.class, new MoteColor()); + moteList.setDefaultEditor(Color.class, new ColorCellEditor("Pick Mote Color")); + moteList.setPreferredScrollableViewportSize(new Dimension(100, 400)); + JScrollPane motePanel = new JScrollPane(); + motePanel.getViewport().add(moteList, null); + main.add(motePanel, BorderLayout.WEST); + + graph = new Graph(this); + main.add(graph, BorderLayout.CENTER); + + // Controls. Organised using box layouts. + + // Sample period. + JLabel sampleLabel = makeLabel("Sample period (ms):", JLabel.RIGHT); + sampleText = makeTextField(6, new ActionListener() { + public void actionPerformed(ActionEvent e) { setSamplePeriod(); } + } ); + updateSamplePeriod(); + + // Clear data. + JButton clearButton = makeButton("Clear data", new ActionListener() { + public void actionPerformed(ActionEvent e) { clearData(); } + } ); + + // Adjust X-axis zoom. + Box xControl = new Box(BoxLayout.Y_AXIS); + xLabel = makeLabel("", JLabel.CENTER); + final JSlider xSlider = new JSlider(JSlider.HORIZONTAL, 0, 8, graph.scale); + Hashtable xTable = new Hashtable(); + for (int i = 0; i <= 8; i += 2) + xTable.put(new Integer(i), + makeSmallLabel("" + (Graph.MIN_WIDTH << i), + JLabel.CENTER)); + xSlider.setLabelTable(xTable); + xSlider.setPaintLabels(true); + graph.updateXLabel(); + graph.setScale(graph.scale); + xSlider.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + //if (!xSlider.getValueIsAdjusting()) + graph.setScale((int)xSlider.getValue()); + } + }); + xControl.add(xLabel); + xControl.add(xSlider); + + // Adjust Y-axis range. + JLabel yLabel = makeLabel("Y:", JLabel.RIGHT); + yText = makeTextField(12, new ActionListener() { + public void actionPerformed(ActionEvent e) { setYAxis(); } + } ); + yText.setText(graph.gy0 + " - " + graph.gy1); + + Box controls = new Box(BoxLayout.X_AXIS); + controls.add(clearButton); + controls.add(Box.createHorizontalGlue()); + controls.add(Box.createRigidArea(new Dimension(20, 0))); + controls.add(sampleLabel); + controls.add(sampleText); + controls.add(Box.createHorizontalGlue()); + controls.add(Box.createRigidArea(new Dimension(20, 0))); + controls.add(xControl); + controls.add(yLabel); + controls.add(yText); + main.add(controls, BorderLayout.SOUTH); + + // The frame part + frame = new JFrame("Oscilloscope"); + frame.setSize(main.getPreferredSize()); + frame.getContentPane().add(main); + frame.setVisible(true); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { System.exit(0); } + }); + } + + /* User operation: clear data */ + void clearData() { + synchronized (parent) { + moteListModel.clear(); + parent.clear(); + graph.newData(); + } + } + + /* User operation: set Y-axis range. */ + void setYAxis() { + String val = yText.getText(); + + try { + int dash = val.indexOf('-'); + if (dash >= 0) { + String min = val.substring(0, dash).trim(); + String max = val.substring(dash + 1).trim(); + + if (!graph.setYAxis(Integer.parseInt(min), Integer.parseInt(max))) + error("Invalid range " + min + " - " + max + " (expected values between 0 and 65535)"); + return; + } + } + catch (NumberFormatException e) { } + error("Invalid range " + val + " (expected NN-MM)"); + } + + /* User operation: set sample period. */ + void setSamplePeriod() { + String periodS = sampleText.getText().trim(); + try { + int newPeriod = Integer.parseInt(periodS); + if (parent.setInterval(newPeriod)) + return; + } + catch (NumberFormatException e) { } + error("Invalid sample period " + periodS); + } + + /* Notification: sample period changed. */ + void updateSamplePeriod() { + sampleText.setText("" + parent.interval); + } + + /* Notification: new node. */ + void newNode(int nodeId) { + moteListModel.newNode(nodeId); + } + + /* Notification: new data. */ + void newData() { + graph.newData(); + } + + void error(String msg) { + JOptionPane.showMessageDialog(frame, msg, "Error", + JOptionPane.ERROR_MESSAGE); + } +} diff --git a/apps/tosthreads/apps/TestCollection/java/build.xml b/apps/tosthreads/apps/TestCollection/java/build.xml new file mode 100644 index 00000000..e8fa088b --- /dev/null +++ b/apps/tosthreads/apps/TestCollection/java/build.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/tosthreads/apps/TestCollection/java/oscilloscope.jar b/apps/tosthreads/apps/TestCollection/java/oscilloscope.jar new file mode 100644 index 0000000000000000000000000000000000000000..acf8fe3a0f08ddfc98fea085a6205e8f3443a6f2 GIT binary patch literal 19256 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/dev/null 2>/dev/null; then + CLASSPATH="oscilloscope.jar;$CLASSPATH" +else + CLASSPATH="oscilloscope.jar:$CLASSPATH" +fi +java Oscilloscope diff --git a/apps/tosthreads/capps/TestCollection/java/ColorCellEditor.java b/apps/tosthreads/capps/TestCollection/java/ColorCellEditor.java new file mode 100644 index 00000000..f88b0b6f --- /dev/null +++ b/apps/tosthreads/capps/TestCollection/java/ColorCellEditor.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006 Intel Corporation + * All rights reserved. + * + * This file is distributed under the terms in the attached INTEL-LICENSE + * file. If you do not find these files, copies can be found by writing to + * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, + * 94704. Attention: Intel License Inquiry. + */ + +import javax.swing.*; +import javax.swing.table.*; +import java.awt.*; +import java.awt.event.*; + +/* Editor for table cells representing colors. Popup a color chooser. */ +public class ColorCellEditor extends AbstractCellEditor + implements TableCellEditor { + private Color color; + private JButton button; + + public ColorCellEditor(String title) { + button = new JButton(); + final JColorChooser chooser = new JColorChooser(); + final JDialog dialog = JColorChooser.createDialog + (button, title, true, chooser, + new ActionListener() { + public void actionPerformed(ActionEvent e) { + color = chooser.getColor(); + } }, + null); + + button.setBorderPainted(false); + button.addActionListener + (new ActionListener () { + public void actionPerformed(ActionEvent e) { + button.setBackground(color); + chooser.setColor(color); + dialog.setVisible(true); + fireEditingStopped(); + } } ); + + } + + public Object getCellEditorValue() { return color; } + public Component getTableCellEditorComponent(JTable table, + Object value, + boolean isSelected, + int row, + int column) { + color = (Color)value; + return button; + } +} + diff --git a/apps/tosthreads/capps/TestCollection/java/Data.java b/apps/tosthreads/capps/TestCollection/java/Data.java new file mode 100644 index 00000000..ac35aa72 --- /dev/null +++ b/apps/tosthreads/capps/TestCollection/java/Data.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2006 Intel Corporation + * All rights reserved. + * + * This file is distributed under the terms in the attached INTEL-LICENSE + * file. If you do not find these files, copies can be found by writing to + * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, + * 94704. Attention: Intel License Inquiry. + */ + +import java.util.*; + +/* Hold all data received from motes */ +class Data { + /* The mote data is stored in a flat array indexed by a mote's identifier. + A null value indicates no mote with that identifier. */ + private Node[] nodes = new Node[256]; + private Oscilloscope parent; + + Data(Oscilloscope parent) { + this.parent = parent; + } + + /* Data received from mote nodeId containing NREADINGS samples from + messageId * NREADINGS onwards. Tell parent if this is a new node. */ + void update(int nodeId, int messageId, int readings[]) { + if (nodeId >= nodes.length) { + int newLength = nodes.length * 2; + if (nodeId >= newLength) + newLength = nodeId + 1; + + Node newNodes[] = new Node[newLength]; + System.arraycopy(nodes, 0, newNodes, 0, nodes.length); + nodes = newNodes; + } + Node node = nodes[nodeId]; + if (node == null) { + nodes[nodeId] = node = new Node(nodeId); + parent.newNode(nodeId); + } + node.update(messageId, readings); + } + + /* Return value of sample x for mote nodeId, or -1 for missing data */ + int getData(int nodeId, int x) { + if (nodeId >= nodes.length || nodes[nodeId] == null) + return -1; + return nodes[nodeId].getData(x); + } + + /* Return number of last known sample on mote nodeId. Returns 0 for + unknown motes. */ + int maxX(int nodeId) { + if (nodeId >= nodes.length || nodes[nodeId] == null) + return 0; + return nodes[nodeId].maxX(); + } + + /* Return number of largest known sample on all motes (0 if there are no + motes) */ + int maxX() { + int max = 0; + + for (int i = 0; i < nodes.length; i++) + if (nodes[i] != null) { + int nmax = nodes[i].maxX(); + + if (nmax > max) + max = nmax; + } + + return max; + } +} diff --git a/apps/tosthreads/capps/TestCollection/java/Graph.java b/apps/tosthreads/capps/TestCollection/java/Graph.java new file mode 100644 index 00000000..9a42c1c8 --- /dev/null +++ b/apps/tosthreads/capps/TestCollection/java/Graph.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2006 Intel Corporation + * All rights reserved. + * + * This file is distributed under the terms in the attached INTEL-LICENSE + * file. If you do not find these files, copies can be found by writing to + * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, + * 94704. Attention: Intel License Inquiry. + */ + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.util.*; + +/* Panel for drawing mote-data graphs */ +class Graph extends JPanel +{ + final static int BORDER_LEFT = 40; + final static int BORDER_RIGHT = 0; + final static int BORDER_TOP = 10; + final static int BORDER_BOTTOM = 10; + + final static int TICK_SPACING = 40; + final static int MAX_TICKS = 16; + final static int TICK_WIDTH = 10; + + final static int MIN_WIDTH = 50; + + int gx0, gx1, gy0, gy1; // graph bounds + int scale = 2; // gx1 - gx0 == MIN_WIDTH << scale + Window parent; + + /* Graph to screen coordinate conversion support */ + int height, width; + double xscale, yscale; + + void updateConversion() { + height = getHeight() - BORDER_TOP - BORDER_BOTTOM; + width = getWidth() - BORDER_LEFT - BORDER_RIGHT; + if (height < 1) + height = 1; + if (width < 1) + width = 1; + xscale = (double)width / (gx1 - gx0 + 1); + yscale = (double)height / (gy1 - gy0 + 1); + } + + Graphics makeClip(Graphics g) { + return g.create(BORDER_LEFT, BORDER_TOP, width, height); + } + + // Note that these do not include the border offset! + int screenX(int gx) { + return (int)(xscale * (gx - gx0) + 0.5); + } + + int screenY(int gy) { + return (int)(height - yscale * (gy - gy0)); + } + + int graphX(int sx) { + return (int)(sx / xscale + gx0 + 0.5); + } + + Graph(Window parent) { + this.parent = parent; + gy0 = 0; gy1 = 0xffff; + gx0 = 0; gx1 = MIN_WIDTH << scale; + } + + void rightDrawString(Graphics2D g, String s, int x, int y) { + TextLayout layout = + new TextLayout(s, parent.smallFont, g.getFontRenderContext()); + Rectangle2D bounds = layout.getBounds(); + layout.draw(g, x - (float)bounds.getWidth(), y + (float)bounds.getHeight() / 2); + } + + protected void paintComponent(Graphics g) { + Graphics2D g2d = (Graphics2D)g; + + /* Repaint. Synchronize on Oscilloscope to avoid data changing. + Simply clear panel, draw Y axis and all the mote graphs. */ + synchronized (parent.parent) { + updateConversion(); + g2d.setColor(Color.BLACK); + g2d.fillRect(0, 0, getWidth(), getHeight()); + drawYAxis(g2d); + + Graphics clipped = makeClip(g2d); + int count = parent.moteListModel.size(); + for (int i = 0; i < count; i++) { + clipped.setColor(parent.moteListModel.getColor(i)); + drawGraph(clipped, parent.moteListModel.get(i)); + } + } + } + + /* Draw the Y-axis */ + protected void drawYAxis(Graphics2D g) { + int axis_x = BORDER_LEFT - 1; + int height = getHeight() - BORDER_BOTTOM - BORDER_TOP; + + g.setColor(Color.WHITE); + g.drawLine(axis_x, BORDER_TOP, axis_x, BORDER_TOP + height - 1); + + /* Draw a reasonable set of tick marks */ + int nTicks = height / TICK_SPACING; + if (nTicks > MAX_TICKS) + nTicks = MAX_TICKS; + + int tickInterval = (gy1 - gy0 + 1) / nTicks; + if (tickInterval == 0) + tickInterval = 1; + + /* Tick interval should be of the family A * 10^B, + where A = 1, 2 * or 5. We tend more to rounding A up, to reduce + rather than increase the number of ticks. */ + int B = (int)(Math.log(tickInterval) / Math.log(10)); + int A = (int)(tickInterval / Math.pow(10, B) + 0.5); + if (A > 2) A = 5; + else if (A > 5) A = 10; + + tickInterval = A * (int)Math.pow(10, B); + + /* Ticks are printed at multiples of tickInterval */ + int tick = ((gy0 + tickInterval - 1) / tickInterval) * tickInterval; + while (tick <= gy1) { + int stick = screenY(tick) + BORDER_TOP; + rightDrawString(g, "" + tick, axis_x - TICK_WIDTH / 2 - 2, stick); + g.drawLine(axis_x - TICK_WIDTH / 2, stick, + axis_x - TICK_WIDTH / 2 + TICK_WIDTH, stick); + tick += tickInterval; + } + + } + + /* Draw graph for mote nodeId */ + protected void drawGraph(Graphics g, int nodeId) { + SingleGraph sg = new SingleGraph(g, nodeId); + + if (gx1 - gx0 >= width) // More points than pixels-iterate by pixel + for (int sx = 0; sx < width; sx++) + sg.nextPoint(g, graphX(sx), sx); + else // Less points than pixel-iterate by points + for (int gx = gx0; gx <= gx1; gx++) + sg.nextPoint(g, gx, screenX(gx)); + } + + /* Inner class to simplify drawing a graph. Simplify initialise it, then + feed it the X screen and graph coordinates, from left to right. */ + private class SingleGraph { + int lastsx, lastsy, nodeId; + + /* Start drawing the graph mote id */ + SingleGraph(Graphics g, int id) { + nodeId = id; + lastsx = -1; + lastsy = -1; + } + + /* Next point in mote's graph is at x value gx, screen coordinate sx */ + void nextPoint(Graphics g, int gx, int sx) { + int gy = parent.parent.data.getData(nodeId, gx); + int sy = -1; + + if (gy >= 0) { // Ignore missing values + double rsy = height - yscale * (gy - gy0); + + // Ignore problem values + if (rsy >= -1e6 && rsy <= 1e6) + sy = (int)(rsy + 0.5); + + if (lastsy >= 0 && sy >= 0) + g.drawLine(lastsx, lastsy, sx, sy); + } + lastsx = sx; + lastsy = sy; + } + } + + /* Update X-axis range in GUI */ + void updateXLabel() { + parent.xLabel.setText("X: " + gx0 + " - " + gx1); + } + + /* Ensure that graph is nicely positioned on screen. max is the largest + sample number received from any mote. */ + private void recenter(int max) { + // New data will show up at the 3/4 point + // The 2nd term ensures that gx1 will be >= max + int scrollby = ((gx1 - gx0) >> 2) + (max - gx1); + gx0 += scrollby; + gx1 += scrollby; + if (gx0 < 0) { // don't bother showing negative sample numbers + gx1 -= gx0; + gx0 = 0; + } + updateXLabel(); + } + + /* New data received. Redraw graph, scrolling if necessary */ + void newData() { + int max = parent.parent.data.maxX(); + + if (max > gx1 || max < gx0) // time to scroll + recenter(max); + repaint(); + } + + /* User set the X-axis scale to newScale */ + void setScale(int newScale) { + gx1 = gx0 + (MIN_WIDTH << newScale); + scale = newScale; + recenter(parent.parent.data.maxX()); + repaint(); + } + + /* User attempted to set Y-axis range to newy0..newy1. Refuse bogus + values (return false), or accept, redraw and return true. */ + boolean setYAxis(int newy0, int newy1) { + if (newy0 >= newy1 || newy0 < 0 || newy0 > 65535 || + newy1 < 0 || newy1 > 65535) + return false; + gy0 = newy0; + gy1 = newy1; + repaint(); + return true; + } +} diff --git a/apps/tosthreads/capps/TestCollection/java/Makefile b/apps/tosthreads/capps/TestCollection/java/Makefile new file mode 100644 index 00000000..55c2605b --- /dev/null +++ b/apps/tosthreads/capps/TestCollection/java/Makefile @@ -0,0 +1,21 @@ +GEN=OscilloscopeMsg.java Constants.java + +all: oscilloscope.jar + +oscilloscope.jar: Oscilloscope.class + jar cf $@ *.class + +OscilloscopeMsg.java: ../MultihopOscilloscope.h + mig -target=null -java-classname=OscilloscopeMsg java ../MultihopOscilloscope.h oscilloscope -o $@ + +Constants.java: ../MultihopOscilloscope.h + ncg -target=null -java-classname=Constants java ../MultihopOscilloscope.h NREADINGS DEFAULT_INTERVAL -o $@ + +Oscilloscope.class: $(wildcard *.java) $(GEN) + javac *.java + +clean: + rm -f *.class $(GEN) + +veryclean: clean + rm oscilloscope.jar diff --git a/apps/tosthreads/capps/TestCollection/java/Node.java b/apps/tosthreads/capps/TestCollection/java/Node.java new file mode 100644 index 00000000..cfe8db9e --- /dev/null +++ b/apps/tosthreads/capps/TestCollection/java/Node.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2006 Intel Corporation + * All rights reserved. + * + * This file is distributed under the terms in the attached INTEL-LICENSE + * file. If you do not find these files, copies can be found by writing to + * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, + * 94704. Attention: Intel License Inquiry. + */ + +/** + * Class holding all data received from a mote. + */ +class Node { + /* Data is hold in an array whose size is a multiple of INCREMENT, and + INCREMENT itself must be a multiple of Constant.NREADINGS. This + simplifies handling the extension and clipping of old data + (see setEnd) */ + final static int INCREMENT = 100 * Constants.NREADINGS; + final static int MAX_SIZE = 100 * INCREMENT; // Must be multiple of INCREMENT + + /* The mote's identifier */ + int id; + + /* Data received from the mote. data[0] is the dataStart'th sample + Indexes 0 through dataEnd - dataStart - 1 hold data. + Samples are 16-bit unsigned numbers, -1 indicates missing data. */ + int[] data; + int dataStart, dataEnd; + + Node(int _id) { + id = _id; + } + + /* Update data to hold received samples newDataIndex .. newEnd. + If we receive data with a lower index, we discard newer data + (we assume the mote rebooted). */ + private void setEnd(int newDataIndex, int newEnd) { + if (newDataIndex < dataStart || data == null) { + /* New data is before the start of what we have. Just throw it + all away and start again */ + dataStart = newDataIndex; + data = new int[INCREMENT]; + } + if (newEnd > dataStart + data.length) { + /* Try extending first */ + if (data.length < MAX_SIZE) { + int newLength = (newEnd - dataStart + INCREMENT - 1) / INCREMENT * INCREMENT; + if (newLength >= MAX_SIZE) + newLength = MAX_SIZE; + + int[] newData = new int[newLength]; + System.arraycopy(data, 0, newData, 0, data.length); + data = newData; + + } + if (newEnd > dataStart + data.length) { + /* Still doesn't fit. Squish. + We assume INCREMENT >= (newEnd - newDataIndex), and ensure + that dataStart + data.length - INCREMENT = newDataIndex */ + int newStart = newDataIndex + INCREMENT - data.length; + + if (dataStart + data.length > newStart) + System.arraycopy(data, newStart - dataStart, data, 0, + data.length - (newStart - dataStart)); + dataStart = newStart; + } + } + /* Mark any missing data as invalid */ + for (int i = dataEnd < dataStart ? dataStart : dataEnd; + i < newDataIndex; i++) + data[i - dataStart] = -1; + + /* If we receive a count less than the old count, we assume the old + data is invalid */ + dataEnd = newEnd; + + } + + /* Data received containing NREADINGS samples from messageId * NREADINGS + onwards */ + void update(int messageId, int readings[]) { + int start = messageId * Constants.NREADINGS; + setEnd(start, start + Constants.NREADINGS); + for (int i = 0; i < readings.length; i++) + data[start - dataStart + i] = readings[i]; + } + + /* Return value of sample x, or -1 for missing data */ + int getData(int x) { + if (x < dataStart || x >= dataEnd) + return -1; + else + return data[x - dataStart]; + } + + /* Return number of last known sample */ + int maxX() { + return dataEnd - 1; + } +} diff --git a/apps/tosthreads/capps/TestCollection/java/Oscilloscope.java b/apps/tosthreads/capps/TestCollection/java/Oscilloscope.java new file mode 100644 index 00000000..3db3741f --- /dev/null +++ b/apps/tosthreads/capps/TestCollection/java/Oscilloscope.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2006 Intel Corporation + * All rights reserved. + * + * This file is distributed under the terms in the attached INTEL-LICENSE + * file. If you do not find these files, copies can be found by writing to + * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, + * 94704. Attention: Intel License Inquiry. + */ + +import net.tinyos.message.*; +import net.tinyos.util.*; +import java.io.*; + +/* The "Oscilloscope" demo app. Displays graphs showing data received from + the Oscilloscope mote application, and allows the user to: + - zoom in or out on the X axis + - set the scale on the Y axis + - change the sampling period + - change the color of each mote's graph + - clear all data + + This application is in three parts: + - the Node and Data objects store data received from the motes and support + simple queries + - the Window and Graph and miscellaneous support objects implement the + GUI and graph drawing + - the Oscilloscope object talks to the motes and coordinates the other + objects + + Synchronization is handled through the Oscilloscope object. Any operation + that reads or writes the mote data must be synchronized on Oscilloscope. + Note that the messageReceived method below is synchronized, so no further + synchronization is needed when updating state based on received messages. +*/ +public class Oscilloscope implements MessageListener +{ + MoteIF mote; + Data data; + Window window; + + /* The current sampling period. If we receive a message from a mote + with a newer version, we update our interval. If we receive a message + with an older version, we broadcast a message with the current interval + and version. If the user changes the interval, we increment the + version and broadcast the new interval and version. */ + int interval = Constants.DEFAULT_INTERVAL; + int version = -1; + + /* Main entry point */ + void run() { + data = new Data(this); + window = new Window(this); + window.setup(); + mote = new MoteIF(PrintStreamMessenger.err); + mote.registerListener(new OscilloscopeMsg(), this); + } + + /* The data object has informed us that nodeId is a previously unknown + mote. Update the GUI. */ + void newNode(int nodeId) { + window.newNode(nodeId); + } + + synchronized public void messageReceived(int dest_addr, Message msg) { + if (msg instanceof OscilloscopeMsg) { + OscilloscopeMsg omsg = (OscilloscopeMsg)msg; + + /* Update interval and mote data */ + periodUpdate(omsg.get_version(), omsg.get_interval()); + data.update(omsg.get_id(), omsg.get_count(), omsg.get_readings()); + + /* Inform the GUI that new data showed up */ + window.newData(); + } + } + + /* A potentially new version and interval has been received from the + mote */ + void periodUpdate(int moteVersion, int moteInterval) { + if (moteVersion > version) { + /* It's new. Update our vision of the interval. */ + version = moteVersion; + interval = moteInterval; + window.updateSamplePeriod(); + } + else if (moteVersion < version) { + /* It's old. Update the mote's vision of the interval. */ + sendInterval(); + } + } + + /* The user wants to set the interval to newPeriod. Refuse bogus values + and return false, or accept the change, broadcast it, and return + true */ + synchronized boolean setInterval(int newPeriod) { + if (newPeriod < 1 || newPeriod > 65535) + return false; + interval = newPeriod; + version++; + sendInterval(); + return true; + } + + /* Broadcast a version+interval message. */ + void sendInterval() { + OscilloscopeMsg omsg = new OscilloscopeMsg(); + + omsg.set_version(version); + omsg.set_interval(interval); + try { + mote.send(MoteIF.TOS_BCAST_ADDR, omsg); + } + catch (IOException e) { + window.error("Cannot send message to mote"); + } + } + + /* User wants to clear all data. */ + void clear() { + data = new Data(this); + } + + public static void main(String[] args) { + Oscilloscope me = new Oscilloscope(); + me.run(); + } +} diff --git a/apps/tosthreads/capps/TestCollection/java/Window.java b/apps/tosthreads/capps/TestCollection/java/Window.java new file mode 100644 index 00000000..d7979bf9 --- /dev/null +++ b/apps/tosthreads/capps/TestCollection/java/Window.java @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2006 Intel Corporation + * All rights reserved. + * + * This file is distributed under the terms in the attached INTEL-LICENSE + * file. If you do not find these files, copies can be found by writing to + * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, + * 94704. Attention: Intel License Inquiry. + */ + +import javax.swing.*; +import javax.swing.table.*; +import javax.swing.event.*; +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +/* The main GUI object. Build the GUI and coordinate all user activities */ +class Window +{ + Oscilloscope parent; + Graph graph; + + Font smallFont = new Font("Dialog", Font.PLAIN, 8); + Font boldFont = new Font("Dialog", Font.BOLD, 12); + Font normalFont = new Font("Dialog", Font.PLAIN, 12); + MoteTableModel moteListModel; // GUI view of mote list + JLabel xLabel; // Label displaying X axis range + JTextField sampleText, yText; // inputs for sample period and Y axis range + JFrame frame; + + Window(Oscilloscope parent) { + this.parent = parent; + } + + /* A model for the mote table, and general utility operations on the mote + list */ + class MoteTableModel extends AbstractTableModel { + private ArrayList motes = new ArrayList(); + private ArrayList colors = new ArrayList(); + + /* Initial mote colors cycle through this list. Add more colors if + you want. */ + private Color[] cycle = { + Color.RED, Color.WHITE, Color.GREEN, Color.MAGENTA, + Color.YELLOW, Color.GRAY, Color.YELLOW + }; + int cycleIndex; + + /* TableModel methods for achieving our table appearance */ + public String getColumnName(int col) { + if (col == 0) + return "Mote"; + else + return "Color"; + } + public int getColumnCount() { return 2; } + public synchronized int getRowCount() { return motes.size(); } + public synchronized Object getValueAt(int row, int col) { + if (col == 0) + return motes.get(row); + else + return colors.get(row); + } + public Class getColumnClass(int col) { + return getValueAt(0, col).getClass(); + } + public boolean isCellEditable(int row, int col) { return col == 1; } + public synchronized void setValueAt(Object value, int row, int col) { + colors.set(row, value); + fireTableCellUpdated(row, col); + graph.repaint(); + } + + /* Return mote id of i'th mote */ + int get(int i) { return ((Integer)motes.get(i)).intValue(); } + + /* Return color of i'th mote */ + Color getColor(int i) { return (Color)colors.get(i); } + + /* Return number of motes */ + int size() { return motes.size(); } + + /* Add a new mote */ + synchronized void newNode(int nodeId) { + /* Shock, horror. No binary search. */ + int i, len = motes.size(); + + for (i = 0; ; i++) + if (i == len || nodeId < get(i)) { + motes.add(i, new Integer(nodeId)); + // Cycle through a set of initial colors + colors.add(i, cycle[cycleIndex++ % cycle.length]); + break; + } + fireTableRowsInserted(i, i); + } + + /* Remove all motes */ + void clear() { + motes = new ArrayList(); + colors = new ArrayList(); + fireTableDataChanged(); + } + } + + /* A simple full-color cell */ + static class MoteColor extends JLabel implements TableCellRenderer { + public MoteColor() { setOpaque(true); } + public Component getTableCellRendererComponent + (JTable table, Object color, + boolean isSelected, boolean hasFocus, int row, int column) { + setBackground((Color)color); + return this; + } + } + + /* Convenience methods for making buttons, labels and textfields. + Simplifies code and ensures a consistent style. */ + + JButton makeButton(String label, ActionListener action) { + JButton button = new JButton(); + button.setText(label); + button.setFont(boldFont); + button.addActionListener(action); + return button; + } + + JLabel makeLabel(String txt, int alignment) { + JLabel label = new JLabel(txt, alignment); + label.setFont(boldFont); + return label; + } + + JLabel makeSmallLabel(String txt, int alignment) { + JLabel label = new JLabel(txt, alignment); + label.setFont(smallFont); + return label; + } + + JTextField makeTextField(int columns, ActionListener action) { + JTextField tf = new JTextField(columns); + tf.setFont(normalFont); + tf.setMaximumSize(tf.getPreferredSize()); + tf.addActionListener(action); + return tf; + } + + /* Build the GUI */ + void setup() { + JPanel main = new JPanel(new BorderLayout()); + + main.setMinimumSize(new Dimension(500, 250)); + main.setPreferredSize(new Dimension(800, 400)); + + // Three panels: mote list, graph, controls + moteListModel = new MoteTableModel(); + JTable moteList = new JTable(moteListModel); + moteList.setDefaultRenderer(Color.class, new MoteColor()); + moteList.setDefaultEditor(Color.class, new ColorCellEditor("Pick Mote Color")); + moteList.setPreferredScrollableViewportSize(new Dimension(100, 400)); + JScrollPane motePanel = new JScrollPane(); + motePanel.getViewport().add(moteList, null); + main.add(motePanel, BorderLayout.WEST); + + graph = new Graph(this); + main.add(graph, BorderLayout.CENTER); + + // Controls. Organised using box layouts. + + // Sample period. + JLabel sampleLabel = makeLabel("Sample period (ms):", JLabel.RIGHT); + sampleText = makeTextField(6, new ActionListener() { + public void actionPerformed(ActionEvent e) { setSamplePeriod(); } + } ); + updateSamplePeriod(); + + // Clear data. + JButton clearButton = makeButton("Clear data", new ActionListener() { + public void actionPerformed(ActionEvent e) { clearData(); } + } ); + + // Adjust X-axis zoom. + Box xControl = new Box(BoxLayout.Y_AXIS); + xLabel = makeLabel("", JLabel.CENTER); + final JSlider xSlider = new JSlider(JSlider.HORIZONTAL, 0, 8, graph.scale); + Hashtable xTable = new Hashtable(); + for (int i = 0; i <= 8; i += 2) + xTable.put(new Integer(i), + makeSmallLabel("" + (Graph.MIN_WIDTH << i), + JLabel.CENTER)); + xSlider.setLabelTable(xTable); + xSlider.setPaintLabels(true); + graph.updateXLabel(); + graph.setScale(graph.scale); + xSlider.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + //if (!xSlider.getValueIsAdjusting()) + graph.setScale((int)xSlider.getValue()); + } + }); + xControl.add(xLabel); + xControl.add(xSlider); + + // Adjust Y-axis range. + JLabel yLabel = makeLabel("Y:", JLabel.RIGHT); + yText = makeTextField(12, new ActionListener() { + public void actionPerformed(ActionEvent e) { setYAxis(); } + } ); + yText.setText(graph.gy0 + " - " + graph.gy1); + + Box controls = new Box(BoxLayout.X_AXIS); + controls.add(clearButton); + controls.add(Box.createHorizontalGlue()); + controls.add(Box.createRigidArea(new Dimension(20, 0))); + controls.add(sampleLabel); + controls.add(sampleText); + controls.add(Box.createHorizontalGlue()); + controls.add(Box.createRigidArea(new Dimension(20, 0))); + controls.add(xControl); + controls.add(yLabel); + controls.add(yText); + main.add(controls, BorderLayout.SOUTH); + + // The frame part + frame = new JFrame("Oscilloscope"); + frame.setSize(main.getPreferredSize()); + frame.getContentPane().add(main); + frame.setVisible(true); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { System.exit(0); } + }); + } + + /* User operation: clear data */ + void clearData() { + synchronized (parent) { + moteListModel.clear(); + parent.clear(); + graph.newData(); + } + } + + /* User operation: set Y-axis range. */ + void setYAxis() { + String val = yText.getText(); + + try { + int dash = val.indexOf('-'); + if (dash >= 0) { + String min = val.substring(0, dash).trim(); + String max = val.substring(dash + 1).trim(); + + if (!graph.setYAxis(Integer.parseInt(min), Integer.parseInt(max))) + error("Invalid range " + min + " - " + max + " (expected values between 0 and 65535)"); + return; + } + } + catch (NumberFormatException e) { } + error("Invalid range " + val + " (expected NN-MM)"); + } + + /* User operation: set sample period. */ + void setSamplePeriod() { + String periodS = sampleText.getText().trim(); + try { + int newPeriod = Integer.parseInt(periodS); + if (parent.setInterval(newPeriod)) + return; + } + catch (NumberFormatException e) { } + error("Invalid sample period " + periodS); + } + + /* Notification: sample period changed. */ + void updateSamplePeriod() { + sampleText.setText("" + parent.interval); + } + + /* Notification: new node. */ + void newNode(int nodeId) { + moteListModel.newNode(nodeId); + } + + /* Notification: new data. */ + void newData() { + graph.newData(); + } + + void error(String msg) { + JOptionPane.showMessageDialog(frame, msg, "Error", + JOptionPane.ERROR_MESSAGE); + } +} diff --git a/apps/tosthreads/capps/TestCollection/java/build.xml b/apps/tosthreads/capps/TestCollection/java/build.xml new file mode 100644 index 00000000..e8fa088b --- /dev/null +++ b/apps/tosthreads/capps/TestCollection/java/build.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/apps/tosthreads/capps/TestCollection/java/oscilloscope.jar b/apps/tosthreads/capps/TestCollection/java/oscilloscope.jar new file mode 100644 index 0000000000000000000000000000000000000000..acf8fe3a0f08ddfc98fea085a6205e8f3443a6f2 GIT binary patch literal 19256 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/dev/null 2>/dev/null; then + CLASSPATH="oscilloscope.jar;$CLASSPATH" +else + CLASSPATH="oscilloscope.jar:$CLASSPATH" +fi +java Oscilloscope -- 2.39.2