Tools: remove collect-view
This commit is contained in:
parent
0971088260
commit
88ae175c9a
|
@ -20,8 +20,6 @@ tools/tunslip6
|
||||||
build
|
build
|
||||||
tools/coffee-manager/build/
|
tools/coffee-manager/build/
|
||||||
tools/coffee-manager/coffee.jar
|
tools/coffee-manager/coffee.jar
|
||||||
tools/collect-view/build/
|
|
||||||
tools/collect-view/dist/
|
|
||||||
COOJA.testlog
|
COOJA.testlog
|
||||||
|
|
||||||
# platform build artifacts
|
# platform build artifacts
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
<?xml version="1.0"?>
|
|
||||||
|
|
||||||
<project name="Sensor Data Collect with Contiki" default="dist" basedir=".">
|
|
||||||
<property name="java" location="src"/>
|
|
||||||
<property name="build" location="build"/>
|
|
||||||
<property name="lib" location="lib"/>
|
|
||||||
<property name="dist" location="dist"/>
|
|
||||||
<property name="contiki" location="../.."/>
|
|
||||||
<property name="archive" value="collect-view.jar"/>
|
|
||||||
<property name="main" value="org.contikios.contiki.collect.CollectServer"/>
|
|
||||||
<property name="args" value="" />
|
|
||||||
|
|
||||||
<path id="classpath">
|
|
||||||
<fileset dir="${lib}" includes="**/*.jar"/>
|
|
||||||
</path>
|
|
||||||
|
|
||||||
<manifestclasspath property="jar.classpath" jarfile="${archive}">
|
|
||||||
<classpath refid="classpath" />
|
|
||||||
</manifestclasspath>
|
|
||||||
|
|
||||||
<target name="init">
|
|
||||||
<tstamp/>
|
|
||||||
<mkdir dir="${build}"/>
|
|
||||||
<mkdir dir="${dist}"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="compile" depends="init">
|
|
||||||
<javac srcdir="${java}" destdir="${build}" classpathref="classpath"
|
|
||||||
debug="true" includeantruntime="false" />
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="jar" depends="compile">
|
|
||||||
<jar destfile="${dist}/${archive}" basedir="${build}">
|
|
||||||
<manifest>
|
|
||||||
<attribute name="Main-Class" value="${main}"/>
|
|
||||||
<attribute name="Class-Path" value="${jar.classpath}" />
|
|
||||||
</manifest>
|
|
||||||
</jar>
|
|
||||||
<mkdir dir="${dist}/lib"/>
|
|
||||||
<copy todir="${dist}/lib">
|
|
||||||
<fileset dir="${lib}"/>
|
|
||||||
</copy>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<condition property="collect-view-shell.exists">
|
|
||||||
<available file="${dist}/collect-view-shell.ihex"/>
|
|
||||||
</condition>
|
|
||||||
|
|
||||||
<target name="collect-view-shell.ihex" unless="collect-view-shell.exists" depends="init">
|
|
||||||
<exec dir="${contiki}/examples/collect" executable="make">
|
|
||||||
<arg value="TARGET=sky"/>
|
|
||||||
<arg value="collect-view-shell.ihex"/>
|
|
||||||
</exec>
|
|
||||||
<copy todir="${dist}" file="${contiki}/examples/collect/collect-view-shell.ihex"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="dist" depends="jar,collect-view-shell.ihex">
|
|
||||||
<copy todir="${dist}" file="collect-init.script"/>
|
|
||||||
<mkdir dir="${dist}/tools"/>
|
|
||||||
<copy todir="${dist}/tools">
|
|
||||||
<fileset dir="${contiki}/tools/sky"/>
|
|
||||||
</copy>
|
|
||||||
<copy file="${contiki}/tools/cygwin/cygwin1.dll" todir="${dist}/tools"/>
|
|
||||||
<chmod dir="${dist}/tools" perm="a+x" includes="**/*"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="run" depends="dist">
|
|
||||||
<java fork="yes" dir="${dist}" jar="${dist}/${archive}">
|
|
||||||
<arg line="${args}"/>
|
|
||||||
</java>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
<target name="clean">
|
|
||||||
<delete dir="${build}"/>
|
|
||||||
<delete dir="${dist}"/>
|
|
||||||
</target>
|
|
||||||
|
|
||||||
</project>
|
|
|
@ -1,6 +0,0 @@
|
||||||
echo ~K
|
|
||||||
echo killall
|
|
||||||
sleep 2
|
|
||||||
echo mac 0
|
|
||||||
sleep 2
|
|
||||||
echo time %TIME% | null
|
|
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -1,164 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* CommandConnection
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 5 oct 2010
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class CommandConnection extends SerialConnection {
|
|
||||||
|
|
||||||
protected Process commandProcess;
|
|
||||||
protected String command;
|
|
||||||
|
|
||||||
public CommandConnection(SerialConnectionListener listener) {
|
|
||||||
super(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandConnection(SerialConnectionListener listener, String command) {
|
|
||||||
super(listener);
|
|
||||||
this.command = command;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getConnectionName() {
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCommand() {
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCommand(String command) {
|
|
||||||
this.command = command;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open(String comPort) {
|
|
||||||
close();
|
|
||||||
this.comPort = comPort == null ? "" : comPort;
|
|
||||||
|
|
||||||
String fullCommand = getCommand();
|
|
||||||
|
|
||||||
isClosed = false;
|
|
||||||
try {
|
|
||||||
String[] cmd = fullCommand.split(" ");
|
|
||||||
System.err.println("Running '" + fullCommand + '\'');
|
|
||||||
|
|
||||||
commandProcess = Runtime.getRuntime().exec(cmd);
|
|
||||||
final BufferedReader input = new BufferedReader(new InputStreamReader(commandProcess.getInputStream()));
|
|
||||||
final BufferedReader err = new BufferedReader(new InputStreamReader(commandProcess.getErrorStream()));
|
|
||||||
setSerialOutput(new PrintWriter(new OutputStreamWriter(commandProcess.getOutputStream())));
|
|
||||||
|
|
||||||
/* Start thread listening on standard out */
|
|
||||||
Thread readInput = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
String line;
|
|
||||||
try {
|
|
||||||
while ((line = input.readLine()) != null) {
|
|
||||||
standardData(line);
|
|
||||||
}
|
|
||||||
input.close();
|
|
||||||
System.err.println("SerialConnection command terminated.");
|
|
||||||
closeConnection();
|
|
||||||
} catch (IOException e) {
|
|
||||||
lastError = "Error when reading from SerialConnection command: " + e;
|
|
||||||
System.err.println(lastError);
|
|
||||||
if (!isClosed) {
|
|
||||||
e.printStackTrace();
|
|
||||||
closeConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "read input stream thread");
|
|
||||||
|
|
||||||
/* Start thread listening on standard err */
|
|
||||||
Thread readError = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
String line;
|
|
||||||
try {
|
|
||||||
while ((line = err.readLine()) != null) {
|
|
||||||
errorData(line);
|
|
||||||
}
|
|
||||||
err.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (!isClosed) {
|
|
||||||
System.err.println("Error when reading from SerialConnection command: " + e);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "read error stream thread");
|
|
||||||
|
|
||||||
if (!isOpen) {
|
|
||||||
isOpen = true;
|
|
||||||
serialOpened();
|
|
||||||
}
|
|
||||||
readInput.start();
|
|
||||||
readError.start();
|
|
||||||
} catch (Exception e) {
|
|
||||||
lastError = "Failed to execute '" + fullCommand + "': " + e;
|
|
||||||
System.err.println(lastError);
|
|
||||||
e.printStackTrace();
|
|
||||||
closeConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void standardData(String line) {
|
|
||||||
serialData(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void errorData(String line) {
|
|
||||||
System.err.println("SerialConnection error stream> " + line);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doClose() {
|
|
||||||
if (commandProcess != null) {
|
|
||||||
commandProcess.destroy();
|
|
||||||
commandProcess = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* Configurable
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 24 sep 2010
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public interface Configurable {
|
|
||||||
|
|
||||||
public void updateConfig(Properties config);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* Link
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 3 jul 2008
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class Link {
|
|
||||||
|
|
||||||
public final Node node;
|
|
||||||
|
|
||||||
private double etx;
|
|
||||||
private int quality = 100;
|
|
||||||
private long lastActive = 0L;
|
|
||||||
|
|
||||||
public Link(Node node) {
|
|
||||||
this.node = node;
|
|
||||||
this.lastActive = System.currentTimeMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node getNode() {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getQuality() {
|
|
||||||
return quality;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setQuality(int quality) {
|
|
||||||
this.quality = quality;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getETX() {
|
|
||||||
return etx;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setETX(double etx) {
|
|
||||||
this.etx = etx;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLastActive() {
|
|
||||||
return lastActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLastActive(long time) {
|
|
||||||
this.lastActive = time;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,226 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* Motelist
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 4 jul 2008
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class MoteFinder {
|
|
||||||
|
|
||||||
public static final String MOTELIST_WINDOWS = "./tools/motelist-windows.exe";
|
|
||||||
public static final String MOTELIST_LINUX = "./tools/motelist-linux";
|
|
||||||
public static final String MOTELIST_MACOS = "./tools/motelist-macos";
|
|
||||||
|
|
||||||
private final Pattern motePattern;
|
|
||||||
private final boolean isWindows;
|
|
||||||
private final boolean isMacos;
|
|
||||||
private Process moteListProcess;
|
|
||||||
// private boolean hasVerifiedProcess;
|
|
||||||
private ArrayList<String> comList = new ArrayList<String>();
|
|
||||||
private ArrayList<String> moteList = new ArrayList<String>();
|
|
||||||
|
|
||||||
public MoteFinder() {
|
|
||||||
String osName = System.getProperty("os.name", "").toLowerCase();
|
|
||||||
isWindows = osName.startsWith("win");
|
|
||||||
isMacos = osName.startsWith("mac");
|
|
||||||
motePattern = Pattern.compile("\\s(COM|/dev/[a-zA-Z]+|/dev/tty.usbserial-)(\\d+|[A-Z0-9]+)\\s");
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getMotes() throws IOException {
|
|
||||||
searchForMotes();
|
|
||||||
return getMoteList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getComPorts() throws IOException {
|
|
||||||
searchForMotes();
|
|
||||||
return getComList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void searchForMotes() throws IOException {
|
|
||||||
comList.clear();
|
|
||||||
moteList.clear();
|
|
||||||
// hasVerifiedProcess = false;
|
|
||||||
|
|
||||||
/* Connect to COM using external serialdump application */
|
|
||||||
String fullCommand;
|
|
||||||
if (isWindows) {
|
|
||||||
fullCommand = MOTELIST_WINDOWS;
|
|
||||||
} else if (isMacos) {
|
|
||||||
fullCommand = MOTELIST_MACOS;
|
|
||||||
} else {
|
|
||||||
fullCommand = MOTELIST_LINUX;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String[] cmd = new String[] { fullCommand };
|
|
||||||
moteListProcess = Runtime.getRuntime().exec(cmd);
|
|
||||||
final BufferedReader input = new BufferedReader(new InputStreamReader(moteListProcess.getInputStream()));
|
|
||||||
final BufferedReader err = new BufferedReader(new InputStreamReader(moteListProcess.getErrorStream()));
|
|
||||||
|
|
||||||
/* Start thread listening on stdout */
|
|
||||||
Thread readInput = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
String line;
|
|
||||||
try {
|
|
||||||
while ((line = input.readLine()) != null) {
|
|
||||||
parseIncomingLine(line);
|
|
||||||
}
|
|
||||||
input.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("Exception when reading from motelist");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "read motelist thread");
|
|
||||||
|
|
||||||
/* Start thread listening on stderr */
|
|
||||||
Thread readError = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
String line;
|
|
||||||
try {
|
|
||||||
while ((line = err.readLine()) != null) {
|
|
||||||
System.err.println("Motelist error stream> " + line);
|
|
||||||
}
|
|
||||||
err.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("Exception when reading from motelist error stream: " + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "read motelist error stream thread");
|
|
||||||
|
|
||||||
readInput.start();
|
|
||||||
readError.start();
|
|
||||||
|
|
||||||
// Wait for the motelist program to finish executing
|
|
||||||
readInput.join();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw (IOException) new IOException("Failed to execute '" + fullCommand + "'").initCause(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getComList() {
|
|
||||||
return comList.toArray(new String[comList.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] getMoteList() {
|
|
||||||
return moteList.toArray(new String[moteList.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
if (moteListProcess != null) {
|
|
||||||
moteListProcess.destroy();
|
|
||||||
moteListProcess = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void parseIncomingLine(String line) {
|
|
||||||
if (line.contains("No devices found") || line.startsWith("Reference")) {
|
|
||||||
// No Sky connected or title before connected motes
|
|
||||||
// hasVerifiedProcess = true;
|
|
||||||
} else if (line.startsWith("-------")) {
|
|
||||||
// Separator
|
|
||||||
} else {
|
|
||||||
Matcher matcher = motePattern.matcher(line);
|
|
||||||
if (matcher.find()) {
|
|
||||||
String dev = matcher.group(1);
|
|
||||||
String no = matcher.group(2);
|
|
||||||
String comPort = dev + no;
|
|
||||||
String moteID = comPort;
|
|
||||||
if (isWindows) {
|
|
||||||
// Special handling of mote id under Windows
|
|
||||||
int moteNumber = Integer.parseInt(no);
|
|
||||||
moteID = Integer.toString(moteNumber - 1);
|
|
||||||
}
|
|
||||||
comList.add(comPort);
|
|
||||||
moteList.add(moteID);
|
|
||||||
} else {
|
|
||||||
System.err.println("Motelist> " + line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String selectComPort(Component parent) {
|
|
||||||
MoteFinder finder = new MoteFinder();
|
|
||||||
try {
|
|
||||||
String[] motes = finder.getComPorts();
|
|
||||||
if (motes == null || motes.length == 0) {
|
|
||||||
JOptionPane.showMessageDialog(parent, "Could not find any connected motes.", "No mote found", JOptionPane.ERROR_MESSAGE);
|
|
||||||
return null;
|
|
||||||
} else if (motes.length == 1) {
|
|
||||||
// Only one node found
|
|
||||||
return motes[0];
|
|
||||||
} else {
|
|
||||||
// Several motes found
|
|
||||||
return (String) JOptionPane.showInputDialog(
|
|
||||||
parent, "Found multiple connected motes. Please select serial port:",
|
|
||||||
"Select serial port", JOptionPane.QUESTION_MESSAGE, null, motes, motes[0]);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
JOptionPane.showMessageDialog(parent, "Failed to search for connected motes:\n" + e, "Error", JOptionPane.ERROR_MESSAGE);
|
|
||||||
return null;
|
|
||||||
} finally {
|
|
||||||
finder.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
|
||||||
MoteFinder finder = new MoteFinder();
|
|
||||||
String[] comPorts = args.length > 0 && "-v".equals(args[0]) ?
|
|
||||||
finder.getMotes() : finder.getComPorts();
|
|
||||||
finder.close();
|
|
||||||
if (comPorts == null || comPorts.length == 0) {
|
|
||||||
System.out.println("No motes connected");
|
|
||||||
} else {
|
|
||||||
for(String port: comPorts) {
|
|
||||||
System.out.println("Found Sky at " + port);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,280 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* MoteProgrammer
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 10 jul 2008
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Window;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JDialog;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JProgressBar;
|
|
||||||
import javax.swing.JScrollPane;
|
|
||||||
import javax.swing.JTextArea;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class MoteProgrammer {
|
|
||||||
|
|
||||||
private MoteProgrammerProcess[] processes;
|
|
||||||
private String[] motes;
|
|
||||||
private String firmwareFile;
|
|
||||||
|
|
||||||
private Window parent;
|
|
||||||
private JProgressBar progressBar;
|
|
||||||
protected JTextArea logTextArea;
|
|
||||||
protected JDialog dialog;
|
|
||||||
protected JButton closeButton;
|
|
||||||
private boolean isDone;
|
|
||||||
|
|
||||||
public MoteProgrammer() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Window getParentComponent() {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setParentComponent(Window parent) {
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasMotes() {
|
|
||||||
return motes != null && motes.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getMotes() {
|
|
||||||
return motes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMotes(String[] motes) {
|
|
||||||
this.motes = motes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void searchForMotes() throws IOException {
|
|
||||||
MoteFinder finder = new MoteFinder();
|
|
||||||
motes = finder.getMotes();
|
|
||||||
finder.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFirmwareFile() {
|
|
||||||
return firmwareFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFirmwareFile(String firmwareFile) {
|
|
||||||
this.firmwareFile = firmwareFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void programMotes() throws IOException {
|
|
||||||
if (firmwareFile == null) {
|
|
||||||
throw new IllegalStateException("no firmware");
|
|
||||||
}
|
|
||||||
if (!hasMotes()) {
|
|
||||||
throw new IllegalStateException("no motes");
|
|
||||||
}
|
|
||||||
File fp = new File(firmwareFile);
|
|
||||||
if (!fp.canRead()) {
|
|
||||||
throw new IllegalStateException("can not read firmware file '" + fp.getAbsolutePath() + '\'');
|
|
||||||
}
|
|
||||||
if (parent != null) {
|
|
||||||
// Use GUI
|
|
||||||
dialog = new JDialog(parent, "Mote Programmer");
|
|
||||||
progressBar = new JProgressBar(0, 100);
|
|
||||||
progressBar.setValue(0);
|
|
||||||
progressBar.setString("Programming...");
|
|
||||||
progressBar.setStringPainted(true);
|
|
||||||
progressBar.setIndeterminate(true);
|
|
||||||
dialog.getContentPane().add(progressBar, BorderLayout.NORTH);
|
|
||||||
|
|
||||||
logTextArea = new JTextArea(28, 80);
|
|
||||||
logTextArea.setEditable(false);
|
|
||||||
logTextArea.setLineWrap(true);
|
|
||||||
dialog.getContentPane().add(new JScrollPane(logTextArea), BorderLayout.CENTER);
|
|
||||||
JPanel panel = new JPanel();
|
|
||||||
closeButton = new JButton("Cancel");
|
|
||||||
closeButton.addActionListener(new ActionListener() {
|
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
MoteProgrammer.this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
panel.add(closeButton);
|
|
||||||
dialog.getContentPane().add(panel, BorderLayout.SOUTH);
|
|
||||||
dialog.pack();
|
|
||||||
dialog.setLocationRelativeTo(parent);
|
|
||||||
dialog.setVisible(true);
|
|
||||||
}
|
|
||||||
processes = new MoteProgrammerProcess[motes.length];
|
|
||||||
isDone = false;
|
|
||||||
try {
|
|
||||||
log("Programming " + motes.length + " motes with '" + firmwareFile + '\'', null);
|
|
||||||
for (int i = 0, n = processes.length; i < n; i++) {
|
|
||||||
processes[i] = new MoteProgrammerProcess(motes[i], firmwareFile) {
|
|
||||||
protected void logLine(String line, boolean stderr, Throwable e) {
|
|
||||||
if (!handleLogLine(this, line, stderr, e)) {
|
|
||||||
super.logLine(line, stderr, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected void processEnded() {
|
|
||||||
handleProcessEnded(this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
processes[i].start();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw (IOException) new IOException("Failed to program motes").initCause(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void waitForProcess() throws InterruptedException {
|
|
||||||
while (!isDone) {
|
|
||||||
wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
MoteProgrammerProcess[] processes = this.processes;
|
|
||||||
if (processes != null) {
|
|
||||||
this.processes = null;
|
|
||||||
for (int i = 0, n = processes.length; i < n; i++) {
|
|
||||||
if (processes[i] != null) {
|
|
||||||
processes[i].stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dialog != null) {
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
dialog.setVisible(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
isDone = true;
|
|
||||||
synchronized (this) {
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void handleProcessEnded(MoteProgrammerProcess process) {
|
|
||||||
// Another process has finished
|
|
||||||
log("Mote@" + process.getMoteID() + "> finished" + (process.hasError() ? " with errors": ""), null);
|
|
||||||
MoteProgrammerProcess[] processes = this.processes;
|
|
||||||
if (processes != null) {
|
|
||||||
int running = 0;
|
|
||||||
int errors = 0;
|
|
||||||
for(MoteProgrammerProcess p: processes) {
|
|
||||||
if (p.isRunning()) {
|
|
||||||
running++;
|
|
||||||
} else if (p.hasError()) {
|
|
||||||
errors++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (running == 0) {
|
|
||||||
// All processes has finished
|
|
||||||
isDone = true;
|
|
||||||
final String doneMessage = "Programming finished with " + errors + " errors.";
|
|
||||||
log(doneMessage, null);
|
|
||||||
if (closeButton != null) {
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
progressBar.setValue(100);
|
|
||||||
progressBar.setIndeterminate(false);
|
|
||||||
progressBar.setString(doneMessage);
|
|
||||||
closeButton.setText("Close");
|
|
||||||
}});
|
|
||||||
}
|
|
||||||
synchronized (this) {
|
|
||||||
notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean handleLogLine(MoteProgrammerProcess moteProgrammerProcess,
|
|
||||||
String line, boolean stderr, final Throwable e) {
|
|
||||||
log("Mote@" + moteProgrammerProcess.getMoteID() + "> " + line, e);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void log(String line, final Throwable e) {
|
|
||||||
System.err.println(line);
|
|
||||||
if (e != null) {
|
|
||||||
e.printStackTrace();
|
|
||||||
line += "\n " + e;
|
|
||||||
}
|
|
||||||
final String text = line;
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
int len = logTextArea.getDocument().getLength();
|
|
||||||
if (len == 0) {
|
|
||||||
logTextArea.append(text);
|
|
||||||
} else {
|
|
||||||
logTextArea.append('\n' + text);
|
|
||||||
len++;
|
|
||||||
}
|
|
||||||
logTextArea.setCaretPosition(len + text.length());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
|
||||||
MoteProgrammer mp = new MoteProgrammer();
|
|
||||||
if (args.length < 1 || args.length > 2) {
|
|
||||||
System.err.println("Usage: MoteProgrammer <firmware> [mote]");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
mp.setFirmwareFile(args[0]);
|
|
||||||
if (args.length == 2) {
|
|
||||||
mp.setMotes(new String[] { args[1] });
|
|
||||||
} else {
|
|
||||||
mp.searchForMotes();
|
|
||||||
}
|
|
||||||
if (!mp.hasMotes()) {
|
|
||||||
System.err.println("No motes connected");
|
|
||||||
System.exit(1);
|
|
||||||
}
|
|
||||||
mp.programMotes();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,226 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* MoteProgrammerProcess
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 10 jul 2008
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class MoteProgrammerProcess {
|
|
||||||
|
|
||||||
public static final String BSL_WINDOWS = "./tools/msp430-bsl-windows.exe";
|
|
||||||
public static final String BSL_LINUX = "./tools/msp430-bsl-linux";
|
|
||||||
|
|
||||||
private final String moteID;
|
|
||||||
private final String firmwareFile;
|
|
||||||
private final String[][] commandSet;
|
|
||||||
private int retry = 3;
|
|
||||||
|
|
||||||
private Process currentProcess;
|
|
||||||
private Thread commandThread;
|
|
||||||
private boolean isRunning;
|
|
||||||
private boolean hasError;
|
|
||||||
|
|
||||||
public MoteProgrammerProcess(String moteID, String firmwareFile) {
|
|
||||||
this.moteID = moteID;
|
|
||||||
this.firmwareFile = firmwareFile;
|
|
||||||
String osName = System.getProperty("os.name").toLowerCase();
|
|
||||||
String bslCommand;
|
|
||||||
if (osName.startsWith("win")) {
|
|
||||||
bslCommand = BSL_WINDOWS;
|
|
||||||
} else {
|
|
||||||
bslCommand = BSL_LINUX;
|
|
||||||
}
|
|
||||||
commandSet = new String[][] {
|
|
||||||
{ bslCommand, "--telosb", "-c", moteID, "-e" },
|
|
||||||
{ bslCommand, "--telosb", "-c", moteID, "-I", "-p", firmwareFile },
|
|
||||||
{ bslCommand, "--telosb", "-c", moteID, "-r" }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMoteID() {
|
|
||||||
return moteID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFirmwareFile() {
|
|
||||||
return firmwareFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRetry() {
|
|
||||||
return retry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRetry(int retry) {
|
|
||||||
this.retry = retry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isRunning() {
|
|
||||||
return isRunning;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasError() {
|
|
||||||
return hasError;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start() {
|
|
||||||
if (isRunning) {
|
|
||||||
// Already running
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
isRunning = true;
|
|
||||||
commandThread = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
int count = 0;
|
|
||||||
do {
|
|
||||||
if (count > 0) {
|
|
||||||
logLine("An error occurred. Retrying.", true, null);
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
hasError = false;
|
|
||||||
for (int j = 0, m = commandSet.length; j < m && isRunning && !hasError; j++) {
|
|
||||||
runCommand(commandSet[j]);
|
|
||||||
Thread.sleep(2000);
|
|
||||||
}
|
|
||||||
} while (isRunning && hasError && count < retry);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
isRunning = false;
|
|
||||||
processEnded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
commandThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stop() {
|
|
||||||
isRunning = false;
|
|
||||||
Process process = currentProcess;
|
|
||||||
if (process != null) {
|
|
||||||
process.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void waitForProcess() throws InterruptedException {
|
|
||||||
if (isRunning && commandThread != null) {
|
|
||||||
commandThread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void processEnded() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runCommand(String[] cmd) throws IOException, InterruptedException {
|
|
||||||
if (currentProcess != null) {
|
|
||||||
currentProcess.destroy();
|
|
||||||
}
|
|
||||||
currentProcess = Runtime.getRuntime().exec(cmd);
|
|
||||||
final BufferedReader input = new BufferedReader(new InputStreamReader(currentProcess.getInputStream()));
|
|
||||||
final BufferedReader err = new BufferedReader(new InputStreamReader(currentProcess.getErrorStream()));
|
|
||||||
|
|
||||||
/* Start thread listening on stdout */
|
|
||||||
Thread readInput = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
String line;
|
|
||||||
try {
|
|
||||||
while ((line = input.readLine()) != null) {
|
|
||||||
handleLine(line, false);
|
|
||||||
}
|
|
||||||
input.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logLine("Error reading from command", false, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "read stdout thread");
|
|
||||||
|
|
||||||
/* Start thread listening on stderr */
|
|
||||||
Thread readError = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
String line;
|
|
||||||
try {
|
|
||||||
while ((line = err.readLine()) != null) {
|
|
||||||
handleLine(line, true);
|
|
||||||
}
|
|
||||||
err.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
logLine("Error reading from command", true, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "read stderr thread");
|
|
||||||
|
|
||||||
readInput.start();
|
|
||||||
readError.start();
|
|
||||||
|
|
||||||
// Wait for the bsl program to finish executing
|
|
||||||
readInput.join();
|
|
||||||
currentProcess = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleLine(String line, boolean stderr) {
|
|
||||||
if (line.toLowerCase().contains("error")) {
|
|
||||||
hasError = true;
|
|
||||||
}
|
|
||||||
logLine(line, stderr, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void logLine(String line, boolean stderr, Throwable e) {
|
|
||||||
if (stderr) {
|
|
||||||
System.err.println("Programmer@" + moteID + "> " + line);
|
|
||||||
} else {
|
|
||||||
System.out.println("Programmer@" + moteID + "> " + line);
|
|
||||||
}
|
|
||||||
if (e != null) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String toString(String[] cmd) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (int i = 0, n = cmd.length; i < n; i++) {
|
|
||||||
if (i > 0) sb.append(' ');
|
|
||||||
sb.append(cmd[i]);
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,217 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* Node
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 3 jul 2008
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Hashtable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class Node implements Comparable<Node> {
|
|
||||||
|
|
||||||
private static final boolean SINGLE_LINK = true;
|
|
||||||
|
|
||||||
private SensorDataAggregator sensorDataAggregator;
|
|
||||||
private ArrayList<SensorData> sensorDataList = new ArrayList<SensorData>();
|
|
||||||
private ArrayList<Link> links = new ArrayList<Link>();
|
|
||||||
|
|
||||||
private final String id;
|
|
||||||
private final String name;
|
|
||||||
|
|
||||||
private Hashtable<String,Object> objectTable;
|
|
||||||
|
|
||||||
private long lastActive;
|
|
||||||
|
|
||||||
public Node(String nodeID) {
|
|
||||||
this(nodeID, nodeID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node(String nodeID, String nodeName) {
|
|
||||||
this.id = nodeID;
|
|
||||||
this.name = nodeName;
|
|
||||||
sensorDataAggregator = new SensorDataAggregator(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final String getID() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLastActive() {
|
|
||||||
return lastActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLastActive(long lastActive) {
|
|
||||||
this.lastActive = lastActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(Node o) {
|
|
||||||
String i1 = id;
|
|
||||||
String i2 = o.getID();
|
|
||||||
// Shorter id first (4.0 before 10.0)
|
|
||||||
if (i1.length() == i2.length()) {
|
|
||||||
return i1.compareTo(i2);
|
|
||||||
}
|
|
||||||
return i1.length() - i2.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// Attributes
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
|
|
||||||
public Object getAttribute(String key) {
|
|
||||||
return getAttribute(key, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getAttribute(String key, Object defaultValue) {
|
|
||||||
if (objectTable == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Object val = objectTable.get(key);
|
|
||||||
return val == null ? defaultValue : val;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAttribute(String key, Object value) {
|
|
||||||
if (objectTable == null) {
|
|
||||||
objectTable = new Hashtable<String,Object>();
|
|
||||||
}
|
|
||||||
objectTable.put(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearAttributes() {
|
|
||||||
if (objectTable != null) {
|
|
||||||
objectTable.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// SensorData
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
|
|
||||||
public SensorDataAggregator getSensorDataAggregator() {
|
|
||||||
return sensorDataAggregator;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SensorData[] getAllSensorData() {
|
|
||||||
return sensorDataList.toArray(new SensorData[sensorDataList.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeAllSensorData() {
|
|
||||||
sensorDataList.clear();
|
|
||||||
sensorDataAggregator.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public SensorData getSensorData(int index) {
|
|
||||||
return sensorDataList.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSensorDataCount() {
|
|
||||||
return sensorDataList.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean addSensorData(SensorData data) {
|
|
||||||
if (sensorDataList.size() > 0) {
|
|
||||||
SensorData last = sensorDataList.get(sensorDataList.size() - 1);
|
|
||||||
if (data.getNodeTime() < last.getNodeTime()) {
|
|
||||||
// Sensor data already added
|
|
||||||
System.out.println("SensorData: ignoring (time " + (data.getNodeTime() - last.getNodeTime())
|
|
||||||
+ "msec): " + data);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sensorDataList.add(data);
|
|
||||||
sensorDataAggregator.addSensorData(data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// Links
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
|
|
||||||
public Link getLink(Node node) {
|
|
||||||
for(Link l: links) {
|
|
||||||
if (l.node == node) {
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add new link
|
|
||||||
Link l = new Link(node);
|
|
||||||
if (SINGLE_LINK) {
|
|
||||||
links.clear();
|
|
||||||
}
|
|
||||||
links.add(l);
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Link getLink(int index) {
|
|
||||||
return links.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLinkCount() {
|
|
||||||
return links.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeLink(Node node) {
|
|
||||||
for (int i = 0, n = links.size(); i < n; i++) {
|
|
||||||
Link l = links.get(i);
|
|
||||||
if (l.node == node) {
|
|
||||||
links.remove(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearLinks() {
|
|
||||||
links.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,243 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* SensorData
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 3 jul 2008
|
|
||||||
* Updated : $Date: 2011/01/09 21:06:10 $
|
|
||||||
* $Revision: 1.2 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class SensorData implements SensorInfo {
|
|
||||||
|
|
||||||
private final Node node;
|
|
||||||
private final int[] values;
|
|
||||||
private final long nodeTime;
|
|
||||||
private final long systemTime;
|
|
||||||
private int seqno;
|
|
||||||
private boolean isDuplicate;
|
|
||||||
|
|
||||||
public SensorData(Node node, int[] values, long systemTime) {
|
|
||||||
this.node = node;
|
|
||||||
this.values = values;
|
|
||||||
this.nodeTime = ((values[TIMESTAMP1] << 16) + values[TIMESTAMP2]) * 1000L;
|
|
||||||
this.systemTime = systemTime;
|
|
||||||
this.seqno = values[SEQNO];
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node getNode() {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNodeID() {
|
|
||||||
return node.getID();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isDuplicate() {
|
|
||||||
return isDuplicate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDuplicate(boolean isDuplicate) {
|
|
||||||
this.isDuplicate = isDuplicate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSeqno() {
|
|
||||||
return seqno;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSeqno(int seqno) {
|
|
||||||
this.seqno = seqno;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getValue(int index) {
|
|
||||||
return values[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getValueCount() {
|
|
||||||
return values.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getNodeTime() {
|
|
||||||
return nodeTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getSystemTime() {
|
|
||||||
return systemTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
if (systemTime > 0L) {
|
|
||||||
sb.append(systemTime).append(' ');
|
|
||||||
}
|
|
||||||
for (int i = 0, n = values.length; i < n; i++) {
|
|
||||||
if (i > 0) sb.append(' ');
|
|
||||||
sb.append(values[i]);
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SensorData parseSensorData(CollectServer server, String line) {
|
|
||||||
return parseSensorData(server, line, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SensorData parseSensorData(CollectServer server, String line, long systemTime) {
|
|
||||||
String[] components = line.trim().split("[ \t]+");
|
|
||||||
// Check if COOJA log
|
|
||||||
if (components.length == VALUES_COUNT + 2 && components[1].startsWith("ID:")) {
|
|
||||||
if (!components[2].equals("" + VALUES_COUNT)) {
|
|
||||||
// Ignore non sensor data
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
systemTime = Long.parseLong(components[0]);
|
|
||||||
components = Arrays.copyOfRange(components, 2, components.length);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
// First column does not seem to be system time
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (components[0].length() > 8) {
|
|
||||||
// Sensor data prefixed with system time
|
|
||||||
try {
|
|
||||||
systemTime = Long.parseLong(components[0]);
|
|
||||||
components = Arrays.copyOfRange(components, 1, components.length);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
// First column does not seem to be system time
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (components.length != SensorData.VALUES_COUNT) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// Sensor data line (probably)
|
|
||||||
int[] data = parseToInt(components);
|
|
||||||
if (data == null || data[0] != VALUES_COUNT) {
|
|
||||||
System.err.println("Failed to parse data line: '" + line + "'");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
String nodeID = mapNodeID(data[NODE_ID]);
|
|
||||||
Node node = server.addNode(nodeID);
|
|
||||||
return new SensorData(node, data, systemTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String mapNodeID(int nodeID) {
|
|
||||||
return "" + (nodeID & 0xff) + '.' + ((nodeID >> 8) & 0xff);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int[] parseToInt(String[] text) {
|
|
||||||
try {
|
|
||||||
int[] data = new int[text.length];
|
|
||||||
for (int i = 0, n = data.length; i < n; i++) {
|
|
||||||
data[i] = Integer.parseInt(text[i]);
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getCPUPower() {
|
|
||||||
return (values[TIME_CPU] * POWER_CPU) / (values[TIME_CPU] + values[TIME_LPM]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLPMPower() {
|
|
||||||
return (values[TIME_LPM] * POWER_LPM) / (values[TIME_CPU] + values[TIME_LPM]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getListenPower() {
|
|
||||||
return (values[TIME_LISTEN] * POWER_LISTEN) / (values[TIME_CPU] + values[TIME_LPM]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getTransmitPower() {
|
|
||||||
return (values[TIME_TRANSMIT] * POWER_TRANSMIT) / (values[TIME_CPU] + values[TIME_LPM]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getAveragePower() {
|
|
||||||
return (values[TIME_CPU] * POWER_CPU + values[TIME_LPM] * POWER_LPM
|
|
||||||
+ values[TIME_LISTEN] * POWER_LISTEN + values[TIME_TRANSMIT] * POWER_TRANSMIT)
|
|
||||||
/ (values[TIME_CPU] + values[TIME_LPM]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getPowerMeasureTime() {
|
|
||||||
return (1000L * (values[TIME_CPU] + values[TIME_LPM])) / TICKS_PER_SECOND;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getTemperature() {
|
|
||||||
return -39.6 + 0.01 * values[TEMPERATURE];
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getBatteryVoltage() {
|
|
||||||
return values[BATTERY_VOLTAGE] * 2 * 2.5 / 4096.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getBatteryIndicator() {
|
|
||||||
return values[BATTERY_INDICATOR];
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getRadioIntensity() {
|
|
||||||
return values[RSSI];
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLatency() {
|
|
||||||
return values[LATENCY] / 32678.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getHumidity() {
|
|
||||||
double v = -4.0 + 405.0 * values[HUMIDITY] / 10000.0;
|
|
||||||
if(v > 100) {
|
|
||||||
return 100;
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLight1() {
|
|
||||||
return 10.0 * values[LIGHT1] / 7.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLight2() {
|
|
||||||
return 46.0 * values[LIGHT2] / 10.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getBestNeighborID() {
|
|
||||||
return values[BEST_NEIGHBOR] > 0 ? mapNodeID(values[BEST_NEIGHBOR]): null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getBestNeighborETX() {
|
|
||||||
return values[BEST_NEIGHBOR_ETX] / 8.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,320 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* SensorDataAggregator
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 20 aug 2008
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class SensorDataAggregator implements SensorInfo {
|
|
||||||
|
|
||||||
private final Node node;
|
|
||||||
private long[] values;
|
|
||||||
private int minSeqno = Integer.MAX_VALUE;
|
|
||||||
private int maxSeqno = Integer.MIN_VALUE;
|
|
||||||
private int seqnoDelta = 0;
|
|
||||||
private int dataCount;
|
|
||||||
private int duplicates = 0;
|
|
||||||
private int lost = 0;
|
|
||||||
private int nodeRestartCount = 0;
|
|
||||||
private int nextHopChangeCount = 0;
|
|
||||||
private int lastNextHop = -1;
|
|
||||||
private long shortestPeriod = Long.MAX_VALUE;
|
|
||||||
private long longestPeriod = 0;
|
|
||||||
|
|
||||||
public SensorDataAggregator(Node node) {
|
|
||||||
this.node = node;
|
|
||||||
this.values = new long[VALUES_COUNT];
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node getNode() {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getNodeID() {
|
|
||||||
return node.getID();
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getValue(int index) {
|
|
||||||
return values[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getAverageValue(int index) {
|
|
||||||
return dataCount > 0 ? (double)values[index] / (double)dataCount : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getValueCount() {
|
|
||||||
return values.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDataCount() {
|
|
||||||
return dataCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addSensorData(SensorData data) {
|
|
||||||
int seqn = data.getValue(SEQNO);
|
|
||||||
int s = seqn + seqnoDelta;
|
|
||||||
|
|
||||||
int bestNeighbor = data.getValue(BEST_NEIGHBOR);
|
|
||||||
if (lastNextHop != bestNeighbor && lastNextHop >= 0) {
|
|
||||||
nextHopChangeCount++;
|
|
||||||
}
|
|
||||||
lastNextHop = bestNeighbor;
|
|
||||||
|
|
||||||
if (s <= maxSeqno) {
|
|
||||||
// Check for duplicates among the last 5 packets
|
|
||||||
for(int n = node.getSensorDataCount() - 1, i = n > 5 ? n - 5 : 0; i < n; i++) {
|
|
||||||
SensorData sd = node.getSensorData(i);
|
|
||||||
if (sd.getValue(SEQNO) != seqn || sd == data || sd.getValueCount() != data.getValueCount()) {
|
|
||||||
// Not a duplicate
|
|
||||||
} else if (Math.abs(data.getNodeTime() - sd.getNodeTime()) > 180000) {
|
|
||||||
// Too long time between packets. Not a duplicate.
|
|
||||||
// System.err.println("Too long time between packets with same seqno from "
|
|
||||||
// + data.getNode() + ": "
|
|
||||||
// + (Math.abs(data.getNodeTime() - sd.getNodeTime()) / 1000)
|
|
||||||
// + " sec, " + (n - i) + " packets ago");
|
|
||||||
} else {
|
|
||||||
data.setDuplicate(true);
|
|
||||||
|
|
||||||
// Verify that the packet is a duplicate
|
|
||||||
for(int j = DATA_LEN2, m = data.getValueCount(); j < m; j++) {
|
|
||||||
if (sd.getValue(j) != data.getValue(j)) {
|
|
||||||
data.setDuplicate(false);
|
|
||||||
// System.out.println("NOT Duplicate: " + data.getNode() + " ("
|
|
||||||
// + (n - i) + ": "
|
|
||||||
// + ((data.getNodeTime() - sd.getNodeTime()) / 1000) + "sek): "
|
|
||||||
// + seqn + " value[" + j + "]: " + sd.getValue(j) + " != "
|
|
||||||
// + data.getValue(j));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (data.isDuplicate()) {
|
|
||||||
// System.out.println("Duplicate: " + data.getNode() + ": " + seqn
|
|
||||||
// + ": "
|
|
||||||
// + (Math.abs(data.getNodeTime() - sd.getNodeTime()) / 1000)
|
|
||||||
// + " sec, " + (n - i) + " packets ago");
|
|
||||||
duplicates++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data.isDuplicate()) {
|
|
||||||
for (int i = 0, n = Math.min(VALUES_COUNT, data.getValueCount()); i < n; i++) {
|
|
||||||
values[i] += data.getValue(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.getSensorDataCount() > 1) {
|
|
||||||
long timeDiff = data.getNodeTime() - node.getSensorData(node.getSensorDataCount() - 2).getNodeTime();
|
|
||||||
if (timeDiff > longestPeriod) {
|
|
||||||
longestPeriod = timeDiff;
|
|
||||||
}
|
|
||||||
if (timeDiff < shortestPeriod) {
|
|
||||||
shortestPeriod = timeDiff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dataCount == 0) {
|
|
||||||
// First packet from node.
|
|
||||||
} else if (maxSeqno - s > 2) {
|
|
||||||
// Handle sequence number overflow.
|
|
||||||
seqnoDelta = maxSeqno + 1;
|
|
||||||
s = seqnoDelta + seqn;
|
|
||||||
if (seqn > 127) {
|
|
||||||
// Sequence number restarted at 128 (to separate node restarts
|
|
||||||
// from sequence number overflow).
|
|
||||||
seqn -= 128;
|
|
||||||
seqnoDelta -= 128;
|
|
||||||
s -= 128;
|
|
||||||
} else {
|
|
||||||
// Sequence number restarted at 0. This is usually an indication that
|
|
||||||
// the node restarted.
|
|
||||||
nodeRestartCount++;
|
|
||||||
}
|
|
||||||
if (seqn > 0) {
|
|
||||||
lost += seqn;
|
|
||||||
}
|
|
||||||
} else if (s > maxSeqno + 1){
|
|
||||||
lost += s - (maxSeqno + 1);
|
|
||||||
}
|
|
||||||
if (s < minSeqno) minSeqno = s;
|
|
||||||
if (s > maxSeqno) maxSeqno = s;
|
|
||||||
dataCount++;
|
|
||||||
}
|
|
||||||
data.setSeqno(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
for (int i = 0, n = values.length; i < n; i++) {
|
|
||||||
values[i] = 0L;
|
|
||||||
}
|
|
||||||
dataCount = 0;
|
|
||||||
duplicates = 0;
|
|
||||||
lost = 0;
|
|
||||||
nodeRestartCount = 0;
|
|
||||||
nextHopChangeCount = 0;
|
|
||||||
lastNextHop = 0;
|
|
||||||
minSeqno = Integer.MAX_VALUE;
|
|
||||||
maxSeqno = Integer.MIN_VALUE;
|
|
||||||
seqnoDelta = 0;
|
|
||||||
shortestPeriod = Long.MAX_VALUE;
|
|
||||||
longestPeriod = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (int i = 0, n = values.length; i < n; i++) {
|
|
||||||
if (i > 0) sb.append(' ');
|
|
||||||
sb.append(values[i]);
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getCPUPower() {
|
|
||||||
return (values[TIME_CPU] * POWER_CPU) / (values[TIME_CPU] + values[TIME_LPM]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getLPMPower() {
|
|
||||||
return (values[TIME_LPM] * POWER_LPM) / (values[TIME_CPU] + values[TIME_LPM]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getListenPower() {
|
|
||||||
return (values[TIME_LISTEN] * POWER_LISTEN) / (values[TIME_CPU] + values[TIME_LPM]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getTransmitPower() {
|
|
||||||
return (values[TIME_TRANSMIT] * POWER_TRANSMIT) / (values[TIME_CPU] + values[TIME_LPM]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getAveragePower() {
|
|
||||||
return (values[TIME_CPU] * POWER_CPU + values[TIME_LPM] * POWER_LPM
|
|
||||||
+ values[TIME_LISTEN] * POWER_LISTEN + values[TIME_TRANSMIT] * POWER_TRANSMIT)
|
|
||||||
/ (values[TIME_CPU] + values[TIME_LPM]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getAverageDutyCycle(int index) {
|
|
||||||
return (double)(values[index]) / (double)(values[TIME_CPU] + values[TIME_LPM]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getPowerMeasureTime() {
|
|
||||||
return (1000L * (values[TIME_CPU] + values[TIME_LPM])) / TICKS_PER_SECOND;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getAverageTemperature() {
|
|
||||||
return dataCount > 0 ? (-39.6 + 0.01 * (values[TEMPERATURE] / dataCount)) : 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getAverageRtmetric() {
|
|
||||||
return getAverageValue(RTMETRIC);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getAverageRadioIntensity() {
|
|
||||||
return getAverageValue(RSSI);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getAverageLatency() {
|
|
||||||
return getAverageValue(LATENCY) / 4096.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getAverageHumidity() {
|
|
||||||
double v = 0.0;
|
|
||||||
if (dataCount > 0) {
|
|
||||||
v = -4.0 + 405.0 * (values[HUMIDITY] / dataCount) / 10000.0;
|
|
||||||
}
|
|
||||||
return v > 100 ? 100 : v;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getAverageLight1() {
|
|
||||||
return 10.0 * getAverageValue(LIGHT1) / 7.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getAverageLight2() {
|
|
||||||
return 46.0 * getAverageValue(LIGHT2) / 10.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getAverageBestNeighborETX() {
|
|
||||||
return getAverageValue(BEST_NEIGHBOR_ETX) / 8.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPacketCount() {
|
|
||||||
return node.getSensorDataCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNextHopChangeCount() {
|
|
||||||
return nextHopChangeCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getEstimatedRestarts() {
|
|
||||||
return nodeRestartCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getEstimatedLostCount() {
|
|
||||||
return lost;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDuplicateCount() {
|
|
||||||
return duplicates;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMinSeqno() {
|
|
||||||
return minSeqno;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMaxSeqno() {
|
|
||||||
return maxSeqno;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getAveragePeriod() {
|
|
||||||
if (dataCount > 1) {
|
|
||||||
long first = node.getSensorData(0).getNodeTime();
|
|
||||||
long last = node.getSensorData(node.getSensorDataCount() - 1).getNodeTime();
|
|
||||||
return (last - first) / dataCount;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getShortestPeriod() {
|
|
||||||
return shortestPeriod < Long.MAX_VALUE ? shortestPeriod : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getLongestPeriod() {
|
|
||||||
return longestPeriod;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* SensorInfo
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 20 aug 2008
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public interface SensorInfo {
|
|
||||||
|
|
||||||
public static final long TICKS_PER_SECOND = 4096L;
|
|
||||||
public static final double VOLTAGE = 3;
|
|
||||||
public static final double POWER_CPU = 1.800 * VOLTAGE; /* mW */
|
|
||||||
public static final double POWER_LPM = 0.0545 * VOLTAGE; /* mW */
|
|
||||||
public static final double POWER_TRANSMIT = 17.7 * VOLTAGE; /* mW */
|
|
||||||
public static final double POWER_LISTEN = 20.0 * VOLTAGE; /* mW */
|
|
||||||
|
|
||||||
public static final int DATA_LEN = 0;
|
|
||||||
public static final int TIMESTAMP1 = 1;
|
|
||||||
public static final int TIMESTAMP2 = 2;
|
|
||||||
public static final int TIMESYNCTIMESTAMP = 3;
|
|
||||||
public static final int NODE_ID = 4;
|
|
||||||
public static final int SEQNO = 5;
|
|
||||||
public static final int HOPS = 6;
|
|
||||||
public static final int LATENCY = 7;
|
|
||||||
public static final int DATA_LEN2 = 8;
|
|
||||||
public static final int CLOCK = 9;
|
|
||||||
public static final int TIMESYNCHTIME = 10;
|
|
||||||
public static final int TIME_CPU = 11;
|
|
||||||
public static final int TIME_LPM = 12;
|
|
||||||
public static final int TIME_TRANSMIT = 13;
|
|
||||||
public static final int TIME_LISTEN = 14;
|
|
||||||
public static final int BEST_NEIGHBOR = 15;
|
|
||||||
public static final int BEST_NEIGHBOR_ETX = 16;
|
|
||||||
public static final int RTMETRIC = 17;
|
|
||||||
public static final int NUM_NEIGHBORS = 18;
|
|
||||||
public static final int BEACON_INTERVAL = 19;
|
|
||||||
public static final int BATTERY_VOLTAGE = 20;
|
|
||||||
public static final int BATTERY_INDICATOR = 21;
|
|
||||||
public static final int LIGHT1 = 22;
|
|
||||||
public static final int LIGHT2 = 23;
|
|
||||||
public static final int TEMPERATURE = 24;
|
|
||||||
public static final int HUMIDITY = 25;
|
|
||||||
public static final int RSSI = 26;
|
|
||||||
|
|
||||||
public static final int VALUES_COUNT = 30;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,145 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* SerialConnection
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 5 jul 2008
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public abstract class SerialConnection {
|
|
||||||
|
|
||||||
protected final SerialConnectionListener listener;
|
|
||||||
|
|
||||||
protected boolean isSerialOutputSupported = true;
|
|
||||||
|
|
||||||
protected String comPort;
|
|
||||||
protected boolean isOpen;
|
|
||||||
protected boolean isClosed = true;
|
|
||||||
protected String lastError;
|
|
||||||
|
|
||||||
protected PrintWriter serialOutput;
|
|
||||||
|
|
||||||
protected SerialConnection(SerialConnectionListener listener) {
|
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isMultiplePortsSupported() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSerialOutputSupported(boolean isSerialOutputSupported) {
|
|
||||||
this.isSerialOutputSupported = isSerialOutputSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isSerialOutputSupported() {
|
|
||||||
return isSerialOutputSupported;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isOpen() {
|
|
||||||
return isOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isClosed() {
|
|
||||||
return isClosed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract String getConnectionName();
|
|
||||||
|
|
||||||
public String getComPort() {
|
|
||||||
return comPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setComPort(String comPort) {
|
|
||||||
this.comPort = comPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLastError() {
|
|
||||||
return lastError;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected PrintWriter getSerialOutput() {
|
|
||||||
return serialOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setSerialOutput(PrintWriter serialOutput) {
|
|
||||||
this.serialOutput = serialOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeSerialData(String data) {
|
|
||||||
PrintWriter serialOutput = this.serialOutput;
|
|
||||||
if (serialOutput != null) {
|
|
||||||
serialOutput.println(data);
|
|
||||||
serialOutput.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void open(String comPort);
|
|
||||||
|
|
||||||
public final void close() {
|
|
||||||
isClosed = true;
|
|
||||||
lastError = null;
|
|
||||||
closeConnection();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final void closeConnection() {
|
|
||||||
isOpen = false;
|
|
||||||
if (serialOutput != null) {
|
|
||||||
serialOutput.close();
|
|
||||||
serialOutput = null;
|
|
||||||
}
|
|
||||||
doClose();
|
|
||||||
serialClosed();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void doClose();
|
|
||||||
|
|
||||||
protected final void serialData(String line) {
|
|
||||||
listener.serialData(this, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final void serialOpened() {
|
|
||||||
listener.serialOpened(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final void serialClosed() {
|
|
||||||
listener.serialClosed(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* SerialConnectionListener
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 5 oct 2010
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
|
|
||||||
public interface SerialConnectionListener {
|
|
||||||
|
|
||||||
public void serialData(SerialConnection connection, String line);
|
|
||||||
|
|
||||||
public void serialOpened(SerialConnection connection);
|
|
||||||
|
|
||||||
public void serialClosed(SerialConnection connection);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* SerialDumpConnection
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 5 oct 2010
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class SerialDumpConnection extends CommandConnection {
|
|
||||||
|
|
||||||
public static final String SERIALDUMP_WINDOWS = "./tools/serialdump-windows.exe";
|
|
||||||
public static final String SERIALDUMP_LINUX = "./tools/serialdump-linux";
|
|
||||||
public static final String SERIALDUMP_MACOS = "./tools/serialdump-macos";
|
|
||||||
|
|
||||||
public SerialDumpConnection(SerialConnectionListener listener) {
|
|
||||||
super(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isMultiplePortsSupported() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getConnectionName() {
|
|
||||||
return comPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open(String comPort) {
|
|
||||||
if (comPort == null) {
|
|
||||||
throw new IllegalStateException("no com port");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Connect to COM using external serialdump application */
|
|
||||||
String osName = System.getProperty("os.name").toLowerCase();
|
|
||||||
String fullCommand;
|
|
||||||
if (osName.startsWith("win")) {
|
|
||||||
fullCommand = SERIALDUMP_WINDOWS + " " + "-b115200" + " " + getMappedComPortForWindows(comPort);
|
|
||||||
} else if (osName.startsWith("mac")) {
|
|
||||||
fullCommand = SERIALDUMP_MACOS + " " + "-b115200" + " " + comPort;
|
|
||||||
} else {
|
|
||||||
fullCommand = SERIALDUMP_LINUX + " " + "-b115200" + " " + comPort;
|
|
||||||
}
|
|
||||||
setCommand(fullCommand);
|
|
||||||
super.open(comPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void standardData(String line) {
|
|
||||||
serialData(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void errorData(String line) {
|
|
||||||
if (!isOpen && line.startsWith("connecting") && line.endsWith("[OK]")) {
|
|
||||||
isOpen = true;
|
|
||||||
serialOpened();
|
|
||||||
} else {
|
|
||||||
super.errorData(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getMappedComPortForWindows(String comPort) {
|
|
||||||
if (comPort.startsWith("COM")) {
|
|
||||||
comPort = "/dev/com" + comPort.substring(3);
|
|
||||||
}
|
|
||||||
return comPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* StdinConnection
|
|
||||||
*
|
|
||||||
* Authors : Niclas Finne
|
|
||||||
* Created : 5 oct 2010
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class StdinConnection extends SerialConnection {
|
|
||||||
|
|
||||||
private PrintWriter stdout;
|
|
||||||
|
|
||||||
public StdinConnection(SerialConnectionListener listener) {
|
|
||||||
super(listener);
|
|
||||||
// Redirect standard out as standard err to use standard out for serial output
|
|
||||||
stdout = new PrintWriter(new OutputStreamWriter(System.out));
|
|
||||||
System.setOut(System.err);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getConnectionName() {
|
|
||||||
return "<stdin>";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open(String comPort) {
|
|
||||||
close();
|
|
||||||
this.comPort = comPort == null ? "" : comPort;
|
|
||||||
|
|
||||||
isClosed = false;
|
|
||||||
try {
|
|
||||||
final BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
|
|
||||||
setSerialOutput(stdout);
|
|
||||||
|
|
||||||
/* Start thread listening on standard in */
|
|
||||||
Thread readInput = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
String line;
|
|
||||||
try {
|
|
||||||
while ((line = input.readLine()) != null) {
|
|
||||||
serialData(line);
|
|
||||||
// Do not send data too fast
|
|
||||||
try {
|
|
||||||
Thread.sleep(100);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input.close();
|
|
||||||
System.out.println("SerialConnection stdin terminated.");
|
|
||||||
closeConnection();
|
|
||||||
} catch (IOException e) {
|
|
||||||
lastError = "Error when reading from SerialConnection stdin: " + e;
|
|
||||||
System.err.println(lastError);
|
|
||||||
if (!isClosed) {
|
|
||||||
e.printStackTrace();
|
|
||||||
closeConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "read input stream thread");
|
|
||||||
|
|
||||||
isOpen = true;
|
|
||||||
serialOpened();
|
|
||||||
readInput.start();
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
lastError = "Failed to open stdin for reading: " + e;
|
|
||||||
System.err.println(lastError);
|
|
||||||
e.printStackTrace();
|
|
||||||
closeConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doClose() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,130 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2012, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* TCPClientConnection
|
|
||||||
*
|
|
||||||
* Authors : Niclas Finne
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.net.Socket;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class TCPClientConnection extends SerialConnection {
|
|
||||||
|
|
||||||
private final String host;
|
|
||||||
private final int port;
|
|
||||||
|
|
||||||
private Socket client;
|
|
||||||
private BufferedReader in;
|
|
||||||
private PrintStream out;
|
|
||||||
|
|
||||||
public TCPClientConnection(SerialConnectionListener listener, String host, int port) {
|
|
||||||
super(listener);
|
|
||||||
this.host = host;
|
|
||||||
this.port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getConnectionName() {
|
|
||||||
return "<tcp://" + host + ':' + port + '>';
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open(String comPort) {
|
|
||||||
close();
|
|
||||||
this.comPort = comPort == null ? "" : comPort;
|
|
||||||
|
|
||||||
isClosed = false;
|
|
||||||
try {
|
|
||||||
client = new Socket(host, port);
|
|
||||||
in = new BufferedReader(new InputStreamReader(client.getInputStream()));
|
|
||||||
out = new PrintStream(client.getOutputStream());
|
|
||||||
System.out.println("Opened TCP connection to " + host + ':' + port);
|
|
||||||
/* Start thread listening on UDP */
|
|
||||||
Thread readInput = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
String line;
|
|
||||||
while (isOpen && (line = in.readLine()) != null) {
|
|
||||||
serialData(line);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
lastError = "Error when reading from SerialConnection TCP: " + e;
|
|
||||||
System.err.println(lastError);
|
|
||||||
if (!isClosed) {
|
|
||||||
e.printStackTrace();
|
|
||||||
closeConnection();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
System.out.println("SerialConnection TCP terminated.");
|
|
||||||
closeConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "TCP thread");
|
|
||||||
isOpen = true;
|
|
||||||
serialOpened();
|
|
||||||
readInput.start();
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
lastError = "Failed to open TCP connection to " + host + ':' + port + ": " + e;
|
|
||||||
System.err.println(lastError);
|
|
||||||
e.printStackTrace();
|
|
||||||
closeConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doClose() {
|
|
||||||
try {
|
|
||||||
if (in != null) {
|
|
||||||
in.close();
|
|
||||||
in = null;
|
|
||||||
}
|
|
||||||
if (out != null) {
|
|
||||||
out.close();
|
|
||||||
out = null;
|
|
||||||
}
|
|
||||||
if (client != null) {
|
|
||||||
client.close();
|
|
||||||
client = null;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,140 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2011, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* UDPConnection
|
|
||||||
*
|
|
||||||
* Authors : Niclas Finne
|
|
||||||
* Created : 1 June 2011
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.DatagramPacket;
|
|
||||||
import java.net.DatagramSocket;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.io.*;
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class UDPConnection extends SerialConnection {
|
|
||||||
|
|
||||||
private final int port;
|
|
||||||
private DatagramSocket serverSocket;
|
|
||||||
|
|
||||||
public UDPConnection(SerialConnectionListener listener, int port) {
|
|
||||||
super(listener);
|
|
||||||
this.port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getConnectionName() {
|
|
||||||
return "<UDP:" + port + ">";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void open(String comPort) {
|
|
||||||
close();
|
|
||||||
this.comPort = comPort == null ? "" : comPort;
|
|
||||||
|
|
||||||
isClosed = false;
|
|
||||||
try {
|
|
||||||
serverSocket = new DatagramSocket(port);
|
|
||||||
System.out.println("Opened UDP port: " + port);
|
|
||||||
/* Start thread listening on UDP */
|
|
||||||
Thread readInput = new Thread(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
byte[] data = new byte[1024];
|
|
||||||
try {
|
|
||||||
while (isOpen) {
|
|
||||||
DatagramPacket packet = new DatagramPacket(data, data.length);
|
|
||||||
serverSocket.receive(packet);
|
|
||||||
|
|
||||||
InetAddress addr = packet.getAddress();
|
|
||||||
System.out.println("UDP: received " + packet.getLength() + " bytes from " + addr.getHostAddress() + ":" + packet.getPort());
|
|
||||||
|
|
||||||
StringWriter strOut = new StringWriter();
|
|
||||||
PrintWriter out = new PrintWriter(strOut);
|
|
||||||
int payloadLen = packet.getLength() - 2;
|
|
||||||
out.printf("%d", 8 + payloadLen / 2);
|
|
||||||
/* Timestamp. Ignore time synch for now. */
|
|
||||||
long time = System.currentTimeMillis() / 1000;
|
|
||||||
out.printf(" %d %d 0",
|
|
||||||
((time >> 16) & 0xffff), time & 0xffff);
|
|
||||||
byte[] payload = packet.getData();
|
|
||||||
int seqno = payload[0] & 0xff;
|
|
||||||
int hops = 0; /* how to get TTL / hot limit in Java??? */
|
|
||||||
byte[] address = addr.getAddress();
|
|
||||||
/* Ignore latency for now */
|
|
||||||
out.printf(" %d %d %d %d",
|
|
||||||
((address[14] & 0xff) +
|
|
||||||
((address[15] & 0xff) << 8))&0xffff, seqno, hops, 0);
|
|
||||||
int d = 0;
|
|
||||||
for(int i = 0; i < payloadLen ; i += 2) {
|
|
||||||
d = (payload[i + 2] & 0xff) + ((payload[i + 3] & 0xff) << 8);
|
|
||||||
out.printf(" %d", d & 0xffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
String line = strOut.toString();
|
|
||||||
serialData(line);
|
|
||||||
}
|
|
||||||
System.out.println("SerialConnection UDP terminated.");
|
|
||||||
closeConnection();
|
|
||||||
} catch (IOException e) {
|
|
||||||
lastError = "Error when reading from SerialConnection UDP: " + e;
|
|
||||||
System.err.println(lastError);
|
|
||||||
if (!isClosed) {
|
|
||||||
e.printStackTrace();
|
|
||||||
closeConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, "UDP thread");
|
|
||||||
isOpen = true;
|
|
||||||
serialOpened();
|
|
||||||
readInput.start();
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
lastError = "Failed to open UDP server at port " + port + ": " + e;
|
|
||||||
System.err.println(lastError);
|
|
||||||
e.printStackTrace();
|
|
||||||
closeConnection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doClose() {
|
|
||||||
if (serverSocket != null) {
|
|
||||||
serverSocket.close();
|
|
||||||
serverSocket = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* Visualizer
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 3 jul 2008
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect;
|
|
||||||
|
|
||||||
import java.awt.Component;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public interface Visualizer {
|
|
||||||
|
|
||||||
public String getCategory();
|
|
||||||
public String getTitle();
|
|
||||||
public Component getPanel();
|
|
||||||
public void nodesSelected(Node[] node);
|
|
||||||
public void nodeAdded(Node node);
|
|
||||||
public void nodeDataReceived(SensorData sensorData);
|
|
||||||
public void clearNodeData();
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,224 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* PacketChartPanel
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 6 sep 2010
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect.gui;
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
|
|
||||||
import org.jfree.chart.ChartFactory;
|
|
||||||
import org.jfree.chart.ChartPanel;
|
|
||||||
import org.jfree.chart.JFreeChart;
|
|
||||||
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
|
|
||||||
import org.jfree.data.time.Minute;
|
|
||||||
import org.jfree.data.time.TimeSeries;
|
|
||||||
import org.jfree.data.time.TimeSeriesCollection;
|
|
||||||
|
|
||||||
import org.contikios.contiki.collect.CollectServer;
|
|
||||||
import org.contikios.contiki.collect.Node;
|
|
||||||
import org.contikios.contiki.collect.SensorData;
|
|
||||||
import org.contikios.contiki.collect.Visualizer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public abstract class AggregatedTimeChartPanel<T> extends JPanel implements Visualizer {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 2100788758213434540L;
|
|
||||||
|
|
||||||
protected final CollectServer server;
|
|
||||||
protected final String category;
|
|
||||||
protected final String title;
|
|
||||||
protected final TimeSeries series;
|
|
||||||
|
|
||||||
protected final JFreeChart chart;
|
|
||||||
protected final ChartPanel chartPanel;
|
|
||||||
|
|
||||||
private Node[] selectedNodes;
|
|
||||||
private HashMap<Node,T> selectedMap = new HashMap<Node,T>();
|
|
||||||
|
|
||||||
public AggregatedTimeChartPanel(CollectServer server, String category, String title,
|
|
||||||
String timeAxisLabel, String valueAxisLabel) {
|
|
||||||
super(new BorderLayout());
|
|
||||||
this.server = server;
|
|
||||||
this.category = category;
|
|
||||||
this.title = title;
|
|
||||||
this.series = new TimeSeries(title, Minute.class);
|
|
||||||
TimeSeriesCollection timeSeries = new TimeSeriesCollection(series);
|
|
||||||
this.chart = ChartFactory.createTimeSeriesChart(
|
|
||||||
title, timeAxisLabel, valueAxisLabel, timeSeries,
|
|
||||||
false, true, false
|
|
||||||
);
|
|
||||||
this.chartPanel = new ChartPanel(chart);
|
|
||||||
this.chartPanel.setPreferredSize(new Dimension(500, 270));
|
|
||||||
setBaseShapeVisible(false);
|
|
||||||
add(chartPanel, BorderLayout.CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCategory() {
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTitle() {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getPanel() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeAdded(Node node) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodesSelected(Node[] nodes) {
|
|
||||||
if (isVisible()) {
|
|
||||||
updateSelected(nodes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSelected(Node[] nodes) {
|
|
||||||
if (this.selectedNodes != nodes) {
|
|
||||||
this.selectedNodes = nodes;
|
|
||||||
this.selectedMap.clear();
|
|
||||||
if (nodes != null) {
|
|
||||||
for(Node node : nodes) {
|
|
||||||
this.selectedMap.put(node, createState(node));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateCharts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeDataReceived(SensorData data) {
|
|
||||||
if (isVisible() && selectedMap.get(data.getNode()) != null) {
|
|
||||||
updateCharts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearNodeData() {
|
|
||||||
if (isVisible()) {
|
|
||||||
updateCharts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateCharts() {
|
|
||||||
int duplicates = 0;
|
|
||||||
int total = 0;
|
|
||||||
series.clear();
|
|
||||||
if (this.selectedNodes != null && server.getSensorDataCount() > 0) {
|
|
||||||
long minute = server.getSensorData(0).getNodeTime() / 60000;
|
|
||||||
long lastMinute = minute;
|
|
||||||
int count = 0;
|
|
||||||
clearState(selectedMap);
|
|
||||||
for(int i = 0; i < server.getSensorDataCount(); i++) {
|
|
||||||
SensorData sd = server.getSensorData(i);
|
|
||||||
T nodeState = selectedMap.get(sd.getNode());
|
|
||||||
if (nodeState != null) {
|
|
||||||
if (sd.isDuplicate()) {
|
|
||||||
duplicates++;
|
|
||||||
} else {
|
|
||||||
long min = sd.getNodeTime() / 60000;
|
|
||||||
if (min != minute) {
|
|
||||||
if (lastMinute < minute) {
|
|
||||||
series.add(new Minute(new Date(lastMinute * 60000L)), 0);
|
|
||||||
if (lastMinute < minute - 1) {
|
|
||||||
series.add(new Minute(new Date((minute - 1) * 60000L)), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
series.add(new Minute(new Date(minute * 60000L)), getTotalDataValue(count));
|
|
||||||
count = 0;
|
|
||||||
lastMinute = minute + 1;
|
|
||||||
minute = min;
|
|
||||||
}
|
|
||||||
count += getSensorDataValue(sd, nodeState);
|
|
||||||
}
|
|
||||||
total++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chart.setTitle(getTitle(selectedMap.size(), total, duplicates));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getTitle(int nodeCount, int dataCount, int duplicateCount) {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract T createState(Node node);
|
|
||||||
|
|
||||||
protected void clearState(Map<Node,T> map) {
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getTotalDataValue(int value) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract int getSensorDataValue(SensorData sd, T nodeState);
|
|
||||||
|
|
||||||
public boolean getBaseShapeVisible() {
|
|
||||||
return ((XYLineAndShapeRenderer)this.chart.getXYPlot().getRenderer()).getBaseShapesVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBaseShapeVisible(boolean visible) {
|
|
||||||
((XYLineAndShapeRenderer)this.chart.getXYPlot().getRenderer()).setBaseShapesVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getRangeMinimumSize() {
|
|
||||||
return chart.getXYPlot().getRangeAxis().getAutoRangeMinimumSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRangeMinimumSize(double size) {
|
|
||||||
chart.getXYPlot().getRangeAxis().setAutoRangeMinimumSize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVisible(boolean visible) {
|
|
||||||
updateSelected(visible ? server.getSelectedNodes() : null);
|
|
||||||
super.setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,234 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* PowerPanel
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 5 jul 2008
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect.gui;
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.GradientPaint;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import org.jfree.chart.ChartFactory;
|
|
||||||
import org.jfree.chart.ChartPanel;
|
|
||||||
import org.jfree.chart.JFreeChart;
|
|
||||||
import org.jfree.chart.plot.CategoryPlot;
|
|
||||||
import org.jfree.chart.plot.PlotOrientation;
|
|
||||||
import org.jfree.chart.renderer.category.BarRenderer;
|
|
||||||
import org.jfree.data.category.DefaultCategoryDataset;
|
|
||||||
import org.contikios.contiki.collect.CollectServer;
|
|
||||||
import org.contikios.contiki.collect.Node;
|
|
||||||
import org.contikios.contiki.collect.SensorData;
|
|
||||||
import org.contikios.contiki.collect.Visualizer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public abstract class BarChartPanel extends JPanel implements Visualizer {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 7664283678708048061L;
|
|
||||||
|
|
||||||
protected final CollectServer server;
|
|
||||||
protected final String category;
|
|
||||||
protected final String title;
|
|
||||||
protected final String[] categories;
|
|
||||||
protected final JFreeChart chart;
|
|
||||||
protected final ChartPanel chartPanel;
|
|
||||||
protected final DefaultCategoryDataset dataset;
|
|
||||||
|
|
||||||
private boolean isShowingAllNodes = false;
|
|
||||||
private int categoryOrder = 0;
|
|
||||||
|
|
||||||
protected BarChartPanel(CollectServer server, String category, String title,
|
|
||||||
String chartTitle, String domainAxisLabel, String valueAxisLabel,
|
|
||||||
String[] categories) {
|
|
||||||
this(server, category, title, chartTitle, domainAxisLabel, valueAxisLabel, categories, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected BarChartPanel(CollectServer server, String category, String title,
|
|
||||||
String chartTitle, String domainAxisLabel, String valueAxisLabel,
|
|
||||||
String[] categories, boolean stackedChart) {
|
|
||||||
super(new BorderLayout());
|
|
||||||
this.server = server;
|
|
||||||
this.category = category;
|
|
||||||
this.title = title;
|
|
||||||
this.categories = categories;
|
|
||||||
|
|
||||||
/* Create chart with power of all nodes */
|
|
||||||
dataset = new DefaultCategoryDataset();
|
|
||||||
if (stackedChart) {
|
|
||||||
this.chart = ChartFactory.createStackedBarChart(chartTitle,
|
|
||||||
domainAxisLabel, valueAxisLabel, dataset, PlotOrientation.VERTICAL,
|
|
||||||
categories.length > 1, true, false);
|
|
||||||
} else {
|
|
||||||
this.chart = ChartFactory.createBarChart(chartTitle,
|
|
||||||
domainAxisLabel, valueAxisLabel, dataset, PlotOrientation.VERTICAL,
|
|
||||||
categories.length > 1, true, false);
|
|
||||||
if (categories.length > 1) {
|
|
||||||
CategoryPlot plot = chart.getCategoryPlot();
|
|
||||||
BarRenderer renderer = (BarRenderer) plot.getRenderer();
|
|
||||||
renderer.setItemMargin(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.chartPanel = new ChartPanel(chart, false);
|
|
||||||
this.chartPanel.setPreferredSize(new Dimension(500, 270));
|
|
||||||
if (categories.length > 1) {
|
|
||||||
this.chartPanel.addMouseListener(new MouseAdapter() {
|
|
||||||
public void mouseClicked(MouseEvent e) {
|
|
||||||
categoryOrder++;
|
|
||||||
updateCharts();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
CategoryPlot plot = (CategoryPlot) chart.getPlot();
|
|
||||||
BarRenderer renderer = (BarRenderer) plot.getRenderer();
|
|
||||||
if (categories.length < 3) {
|
|
||||||
renderer.setDrawBarOutline(false);
|
|
||||||
|
|
||||||
GradientPaint gp = new GradientPaint(0.0f, 0.0f, Color.RED,
|
|
||||||
0.0f, 0.0f, new Color(128, 0, 0));
|
|
||||||
renderer.setSeriesPaint(0, gp);
|
|
||||||
if (categories.length > 1) {
|
|
||||||
gp = new GradientPaint(0.0f, 0.0f, Color.BLUE,
|
|
||||||
0.0f, 0.0f, new Color(0, 0, 128));
|
|
||||||
renderer.setSeriesPaint(1, gp);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
renderer.setDrawBarOutline(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
add(chartPanel, BorderLayout.CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCategory() {
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTitle() {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getPanel() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isShowingAllNodes() {
|
|
||||||
return isShowingAllNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShowingAllNodes(boolean isShowingAllNodes) {
|
|
||||||
if (this.isShowingAllNodes != isShowingAllNodes) {
|
|
||||||
this.isShowingAllNodes = isShowingAllNodes;
|
|
||||||
if (isVisible()) {
|
|
||||||
updateCharts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeAdded(Node node) {
|
|
||||||
if (isVisible()) {
|
|
||||||
int count = node.getSensorDataCount();
|
|
||||||
if (count > 0 || isShowingAllNodes) {
|
|
||||||
addNode(node);
|
|
||||||
}
|
|
||||||
if (count > 0) {
|
|
||||||
addSensorData(node.getSensorData(count - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodesSelected(Node[] nodes) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeDataReceived(SensorData data) {
|
|
||||||
if (isVisible()) {
|
|
||||||
addSensorData(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearNodeData() {
|
|
||||||
if (isVisible()) {
|
|
||||||
updateCharts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateCharts() {
|
|
||||||
dataset.clear();
|
|
||||||
Node[] nodes = server.getNodes();
|
|
||||||
if (nodes != null) {
|
|
||||||
for (int i = 0, n = nodes.length; i < n; i++) {
|
|
||||||
int count = nodes[i].getSensorDataCount();
|
|
||||||
if (count > 0 || isShowingAllNodes) {
|
|
||||||
addNode(nodes[i]);
|
|
||||||
}
|
|
||||||
if (count > 0) {
|
|
||||||
addSensorData(nodes[i].getSensorData(count - 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addNode(Node node) {
|
|
||||||
String name = node.getName();
|
|
||||||
for (int j = 0, m = categories.length; j < m; j++) {
|
|
||||||
dataset.addValue(0, categories[(j + categoryOrder) % categories.length], name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVisible(boolean visible) {
|
|
||||||
if (visible) {
|
|
||||||
updateCharts();
|
|
||||||
} else {
|
|
||||||
dataset.clear();
|
|
||||||
}
|
|
||||||
super.setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void addSensorData(SensorData data);
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,851 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* MapPanel
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 3 jul 2008
|
|
||||||
* Updated : $Date: 2010/11/23 16:21:48 $
|
|
||||||
* $Revision: 1.4 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect.gui;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.FontMetrics;
|
|
||||||
import java.awt.Graphics;
|
|
||||||
import java.awt.Graphics2D;
|
|
||||||
import java.awt.GridLayout;
|
|
||||||
import java.awt.Polygon;
|
|
||||||
import java.awt.RenderingHints;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.ComponentAdapter;
|
|
||||||
import java.awt.event.ComponentEvent;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.awt.event.MouseListener;
|
|
||||||
import java.awt.event.MouseMotionListener;
|
|
||||||
import java.beans.PropertyChangeEvent;
|
|
||||||
import java.beans.PropertyChangeListener;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Hashtable;
|
|
||||||
import java.util.Properties;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.ImageIcon;
|
|
||||||
import javax.swing.JCheckBoxMenuItem;
|
|
||||||
import javax.swing.JFormattedTextField;
|
|
||||||
import javax.swing.JMenuItem;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JPopupMenu;
|
|
||||||
import javax.swing.JSlider;
|
|
||||||
import javax.swing.Timer;
|
|
||||||
import javax.swing.border.LineBorder;
|
|
||||||
import javax.swing.border.TitledBorder;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
import javax.swing.plaf.basic.BasicGraphicsUtils;
|
|
||||||
|
|
||||||
import org.contikios.contiki.collect.CollectServer;
|
|
||||||
import org.contikios.contiki.collect.Configurable;
|
|
||||||
import org.contikios.contiki.collect.Link;
|
|
||||||
import org.contikios.contiki.collect.Node;
|
|
||||||
import org.contikios.contiki.collect.SensorData;
|
|
||||||
import org.contikios.contiki.collect.Visualizer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class MapPanel extends JPanel implements Configurable, Visualizer, ActionListener, MouseListener, MouseMotionListener {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -8256619482599309425L;
|
|
||||||
|
|
||||||
private static final Logger log =
|
|
||||||
Logger.getLogger(MapPanel.class.getName());
|
|
||||||
|
|
||||||
private static final boolean VISUAL_DRAG = true;
|
|
||||||
|
|
||||||
private static final Color LINK_COLOR = new Color(0x40, 0x40, 0xf0, 0xff);
|
|
||||||
|
|
||||||
private static final int delta = 7;
|
|
||||||
|
|
||||||
private final CollectServer server;
|
|
||||||
private final String category;
|
|
||||||
private final boolean isMap;
|
|
||||||
private String title;
|
|
||||||
|
|
||||||
private Timer timer;
|
|
||||||
|
|
||||||
private JPopupMenu popupMenu;
|
|
||||||
private JCheckBoxMenuItem layoutItem;
|
|
||||||
private JCheckBoxMenuItem lockedItem;
|
|
||||||
private JMenuItem shakeItem;
|
|
||||||
// private JCheckBoxMenuItem dragItem;
|
|
||||||
private JCheckBoxMenuItem backgroundItem;
|
|
||||||
private JCheckBoxMenuItem showNetworkItem;
|
|
||||||
private JCheckBoxMenuItem configItem;
|
|
||||||
private JMenuItem resetNetworkItem;
|
|
||||||
private MapNode popupNode;
|
|
||||||
|
|
||||||
private Hashtable<String,MapNode> nodeTable = new Hashtable<String,MapNode>();
|
|
||||||
private MapNode[] nodeList = new MapNode[0];
|
|
||||||
private boolean updateNodeList;
|
|
||||||
|
|
||||||
private MapNode selectedNode;
|
|
||||||
private ArrayList<MapNode> selectedMapNodes = new ArrayList<MapNode>();
|
|
||||||
private Node[] selectedNodes;
|
|
||||||
private MapNode draggedNode;
|
|
||||||
private long draggedTime;
|
|
||||||
|
|
||||||
private ImageIcon mapImage;
|
|
||||||
private String mapName;
|
|
||||||
private boolean showBackground;
|
|
||||||
|
|
||||||
private int layoutRepel = 100;
|
|
||||||
private int layoutAttract = 50;
|
|
||||||
private int layoutGravity = 1;
|
|
||||||
|
|
||||||
private double etxFactor = 1.0;
|
|
||||||
|
|
||||||
private boolean isLayoutActive = true;
|
|
||||||
private boolean hideNetwork = false;
|
|
||||||
|
|
||||||
protected JPanel configPanel;
|
|
||||||
|
|
||||||
public MapPanel(CollectServer server, String title, String category, boolean isMap) {
|
|
||||||
super(null);
|
|
||||||
this.server = server;
|
|
||||||
this.title = title;
|
|
||||||
this.category = category;
|
|
||||||
this.isMap = isMap;
|
|
||||||
setPreferredSize(new Dimension(300, 200));
|
|
||||||
|
|
||||||
popupMenu = new JPopupMenu(getTitle());
|
|
||||||
if (!isMap) {
|
|
||||||
layoutItem = createCheckBoxMenuItem(popupMenu, "Update Layout", isLayoutActive);
|
|
||||||
popupMenu.add(layoutItem);
|
|
||||||
|
|
||||||
lockedItem = createCheckBoxMenuItem(popupMenu, "Fixed Node Position", false);
|
|
||||||
shakeItem = createMenuItem(popupMenu, "Shake Nodes");
|
|
||||||
popupMenu.addSeparator();
|
|
||||||
}
|
|
||||||
|
|
||||||
showNetworkItem = createCheckBoxMenuItem(popupMenu, "Show Network Info", true);
|
|
||||||
resetNetworkItem = createMenuItem(popupMenu, "Reset Network");
|
|
||||||
popupMenu.addSeparator();
|
|
||||||
if (isMap) {
|
|
||||||
backgroundItem = createCheckBoxMenuItem(popupMenu, "Show Background", false);
|
|
||||||
backgroundItem.setEnabled(false);
|
|
||||||
} else {
|
|
||||||
configItem = createCheckBoxMenuItem(popupMenu, "Show Layout Settings", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// popupMenu.addSeparator();
|
|
||||||
// dragItem = new JCheckBoxMenuItem("Visible Drag", true);
|
|
||||||
// popupMenu.add(dragItem);
|
|
||||||
|
|
||||||
setBackground(Color.white);
|
|
||||||
addMouseListener(this);
|
|
||||||
addMouseMotionListener(this);
|
|
||||||
|
|
||||||
if (!isMap) {
|
|
||||||
timer = new Timer(100, this);
|
|
||||||
|
|
||||||
configPanel = new JPanel(new GridLayout(0, 1));
|
|
||||||
configPanel.setBorder(LineBorder.createBlackLineBorder());
|
|
||||||
|
|
||||||
JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 1000, 1000 - layoutAttract);
|
|
||||||
slider.setBorder(new TitledBorder("Attract Factor: " + (1000 - layoutAttract)));
|
|
||||||
slider.addChangeListener(new ChangeListener() {
|
|
||||||
public void stateChanged(ChangeEvent e) {
|
|
||||||
JSlider slider = (JSlider)e.getSource();
|
|
||||||
layoutAttract = 1000 - slider.getValue();
|
|
||||||
((TitledBorder)slider.getBorder()).setTitle("Attract Factor: " + slider.getValue());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
configPanel.add(slider);
|
|
||||||
|
|
||||||
slider = new JSlider(JSlider.HORIZONTAL, 0, 1000, layoutRepel);
|
|
||||||
slider.setBorder(new TitledBorder("Repel Range: " + layoutRepel));
|
|
||||||
slider.addChangeListener(new ChangeListener() {
|
|
||||||
public void stateChanged(ChangeEvent e) {
|
|
||||||
JSlider slider = (JSlider)e.getSource();
|
|
||||||
layoutRepel = slider.getValue();
|
|
||||||
((TitledBorder)slider.getBorder()).setTitle("Repel Range: " + layoutRepel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
configPanel.add(slider);
|
|
||||||
|
|
||||||
slider = new JSlider(JSlider.HORIZONTAL, 0, 100, layoutGravity);
|
|
||||||
slider.setBorder(new TitledBorder("Gravity: " + layoutGravity));
|
|
||||||
slider.addChangeListener(new ChangeListener() {
|
|
||||||
public void stateChanged(ChangeEvent e) {
|
|
||||||
JSlider slider = (JSlider)e.getSource();
|
|
||||||
layoutGravity = slider.getValue();
|
|
||||||
((TitledBorder)slider.getBorder()).setTitle("Gravity: " + layoutGravity);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
configPanel.add(slider);
|
|
||||||
|
|
||||||
final JFormattedTextField etxField = new JFormattedTextField(new Double(etxFactor));
|
|
||||||
etxField.setBorder(BorderFactory.createTitledBorder("ETX factor"));
|
|
||||||
etxField.setColumns(5);
|
|
||||||
etxField.addPropertyChangeListener("value", new PropertyChangeListener() {
|
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
|
||||||
etxFactor = ((Number)etxField.getValue()).doubleValue();
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
configPanel.add(etxField);
|
|
||||||
|
|
||||||
configPanel.setVisible(false);
|
|
||||||
add(configPanel);
|
|
||||||
|
|
||||||
addComponentListener(new ComponentAdapter() {
|
|
||||||
public void componentResized(ComponentEvent ev) {
|
|
||||||
if (configPanel.isVisible()) {
|
|
||||||
updateConfigLayout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMapBackground() {
|
|
||||||
return isMap ? mapName : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean setMapBackground(String image) {
|
|
||||||
if (!isMap) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (image == null) {
|
|
||||||
mapImage = null;
|
|
||||||
mapName = null;
|
|
||||||
backgroundItem.setEnabled(false);
|
|
||||||
backgroundItem.setSelected(false);
|
|
||||||
showBackground = false;
|
|
||||||
repaint();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageIcon ii = new ImageIcon(image);
|
|
||||||
if (ii.getIconWidth() <= 0 || ii.getIconHeight() <= 0) {
|
|
||||||
log.warning("could not find image '" + image + '\'');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
mapImage = ii;
|
|
||||||
mapName = image;
|
|
||||||
setPreferredSize(new Dimension(ii.getIconWidth(), ii.getIconHeight()));
|
|
||||||
showBackground = true;
|
|
||||||
backgroundItem.setEnabled(true);
|
|
||||||
backgroundItem.setSelected(true);
|
|
||||||
repaint();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JCheckBoxMenuItem createCheckBoxMenuItem(JPopupMenu menu, String title, boolean isSelected) {
|
|
||||||
JCheckBoxMenuItem item = new JCheckBoxMenuItem(title, isSelected);
|
|
||||||
item.addActionListener(this);
|
|
||||||
menu.add(item);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JMenuItem createMenuItem(JPopupMenu menu, String title) {
|
|
||||||
JMenuItem item = new JMenuItem(title);
|
|
||||||
item.addActionListener(this);
|
|
||||||
menu.add(item);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVisible(boolean visible) {
|
|
||||||
if (visible) {
|
|
||||||
clear();
|
|
||||||
if (timer != null) {
|
|
||||||
timer.start();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (timer != null) {
|
|
||||||
timer.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
setCursor(Cursor.getDefaultCursor());
|
|
||||||
draggedNode = null;
|
|
||||||
updateSelected();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// Node handling
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
|
|
||||||
public Node getNode(String id) {
|
|
||||||
MapNode node = nodeTable.get(id);
|
|
||||||
return node != null ? node.node : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MapNode getMapNode(String id) {
|
|
||||||
return nodeTable.get(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private MapNode addMapNode(Node nd) {
|
|
||||||
String id = nd.getID();
|
|
||||||
MapNode node = nodeTable.get(id);
|
|
||||||
if (node == null) {
|
|
||||||
node = new MapNode(this, nd);
|
|
||||||
node.y = 10 + (int) (Math.random() * (Math.max(100, getHeight()) - 20));
|
|
||||||
node.x = 10 + (int) (Math.random() * (Math.max(100, getWidth()) - 30));
|
|
||||||
|
|
||||||
String location = server.getConfig(isMap ? id : ("collect.map." + id));
|
|
||||||
if (location != null) {
|
|
||||||
try {
|
|
||||||
String[] pos = location.split(",");
|
|
||||||
node.x = Integer.parseInt(pos[0].trim());
|
|
||||||
node.y = Integer.parseInt(pos[1].trim());
|
|
||||||
node.hasFixedLocation = !isMap;
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("could not parse node location: " + location);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeTable.put(id, node);
|
|
||||||
updateNodeList = true;
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MapNode[] getNodeList() {
|
|
||||||
if (updateNodeList) {
|
|
||||||
synchronized (nodeTable) {
|
|
||||||
updateNodeList = false;
|
|
||||||
nodeList = nodeTable.values().toArray(new MapNode[nodeTable.size()]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nodeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// Visualizer
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCategory() {
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTitle() {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getPanel() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodesSelected(Node[] nodes) {
|
|
||||||
if (selectedNodes != nodes) {
|
|
||||||
selectedNodes = nodes;
|
|
||||||
if (isVisible()) {
|
|
||||||
updateSelected();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateSelected() {
|
|
||||||
if (selectedMapNodes.size() > 0) {
|
|
||||||
for(MapNode node : selectedMapNodes) {
|
|
||||||
node.isSelected = false;
|
|
||||||
}
|
|
||||||
selectedMapNodes.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedNodes == null || selectedNodes.length == 0) {
|
|
||||||
selectedNode = null;
|
|
||||||
} else {
|
|
||||||
for (Node node : selectedNodes) {
|
|
||||||
MapNode mapNode = addMapNode(node);
|
|
||||||
selectedMapNodes.add(mapNode);
|
|
||||||
mapNode.isSelected = true;
|
|
||||||
}
|
|
||||||
selectedNode = selectedMapNodes.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeAdded(Node nd) {
|
|
||||||
addMapNode(nd);
|
|
||||||
if (isVisible()) {
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeDataReceived(SensorData sensorData) {
|
|
||||||
if (isVisible()) {
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearNodeData() {
|
|
||||||
nodeTable.clear();
|
|
||||||
updateNodeList = true;
|
|
||||||
nodesSelected(null);
|
|
||||||
if (isVisible()) {
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// Graphics
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void paintComponent(Graphics g) {
|
|
||||||
Graphics2D g2d = (Graphics2D) g;
|
|
||||||
FontMetrics fm = g.getFontMetrics();
|
|
||||||
int fnHeight = fm.getHeight();
|
|
||||||
int fnDescent = fm.getDescent();
|
|
||||||
int width = getWidth();
|
|
||||||
int height = getHeight();
|
|
||||||
|
|
||||||
g.setColor(getBackground());
|
|
||||||
g.fillRect(0, 0, width, height);
|
|
||||||
if (showBackground && isMap) {
|
|
||||||
mapImage.paintIcon(this, g, 0, 0);
|
|
||||||
}
|
|
||||||
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
|
||||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
|
||||||
|
|
||||||
// Display legend
|
|
||||||
if (!hideNetwork) {
|
|
||||||
int legendWidth = fm.stringWidth("ETX");
|
|
||||||
g.setColor(Color.black);
|
|
||||||
g.drawString("ETX", width - legendWidth - 10, 10 + fnHeight - fnDescent);
|
|
||||||
g.drawRect(width - legendWidth - 30, 8, legendWidth + 24, fnHeight + 4);
|
|
||||||
g.setColor(LINK_COLOR);
|
|
||||||
g2d.drawLine(width - legendWidth - 25, 10 + fnHeight / 2,
|
|
||||||
width - legendWidth - 15, 10 + fnHeight / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
MapNode[] nodes = getNodeList();
|
|
||||||
if (!isMap || !hideNetwork) {
|
|
||||||
g.setColor(LINK_COLOR);
|
|
||||||
for (MapNode n : nodes) {
|
|
||||||
for (int j = 0, mu = n.node.getLinkCount(); j < mu; j++) {
|
|
||||||
Link link = n.node.getLink(j);
|
|
||||||
MapNode linkNode = addMapNode(link.node);
|
|
||||||
int x2 = linkNode.x;
|
|
||||||
int y2 = linkNode.y;
|
|
||||||
g2d.drawLine(n.x, n.y, x2, y2);
|
|
||||||
drawArrow(g, n.x, n.y, x2, y2, 3);
|
|
||||||
if (!hideNetwork) {
|
|
||||||
int xn1, xn2, yn1, yn2;
|
|
||||||
if (n.x <= x2) {
|
|
||||||
xn1 = n.x; xn2 = x2;
|
|
||||||
yn1 = n.y; yn2 = y2;
|
|
||||||
} else {
|
|
||||||
xn1 = x2; xn2 = n.x;
|
|
||||||
yn1 = y2; yn2 = n.y;
|
|
||||||
}
|
|
||||||
int dx = xn1 + (xn2 - xn1) / 2 + 4;
|
|
||||||
int dy = yn1 + (yn2 - yn1) / 2 - fnDescent;
|
|
||||||
if (yn2 < yn1) {
|
|
||||||
dy += fnHeight - fnDescent;
|
|
||||||
}
|
|
||||||
g.drawString(
|
|
||||||
Double.toString(((int) (link.getETX() * etxFactor * 100 + 0.5)) / 100.0),
|
|
||||||
dx, dy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (MapNode n : nodes) {
|
|
||||||
n.paint(g, n.x, n.y);
|
|
||||||
|
|
||||||
g.setColor(Color.black);
|
|
||||||
if (n.isSelected) {
|
|
||||||
BasicGraphicsUtils.drawDashedRect(g, n.x - delta, n.y - delta,
|
|
||||||
2 * delta, 2 * delta);
|
|
||||||
}
|
|
||||||
if (selectedNode != null && selectedNode.message != null) {
|
|
||||||
g.drawString(selectedNode.message, 10, 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Polygon arrowPoly = new Polygon();
|
|
||||||
private void drawArrow(Graphics g, int xSource, int ySource, int xDest, int yDest, int delta) {
|
|
||||||
double dx = xSource - xDest;
|
|
||||||
double dy = ySource - yDest;
|
|
||||||
double dir = Math.atan2(dx, dy);
|
|
||||||
double len = Math.sqrt(dx * dx + dy * dy);
|
|
||||||
dx /= len;
|
|
||||||
dy /= len;
|
|
||||||
len -= delta;
|
|
||||||
xDest = xSource - (int) (dx * len);
|
|
||||||
yDest = ySource - (int) (dy * len);
|
|
||||||
g.drawLine(xDest, yDest, xSource, ySource);
|
|
||||||
|
|
||||||
final int size = 8;
|
|
||||||
arrowPoly.reset();
|
|
||||||
arrowPoly.addPoint(xDest, yDest);
|
|
||||||
arrowPoly.addPoint(xDest + xCor(size, dir + 0.5), yDest + yCor(size, dir + 0.5));
|
|
||||||
arrowPoly.addPoint(xDest + xCor(size, dir - 0.5), yDest + yCor(size, dir - 0.5));
|
|
||||||
arrowPoly.addPoint(xDest, yDest);
|
|
||||||
g.fillPolygon(arrowPoly);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int yCor(int len, double dir) {
|
|
||||||
return (int)(0.5 + len * Math.cos(dir));
|
|
||||||
}
|
|
||||||
|
|
||||||
private int xCor(int len, double dir) {
|
|
||||||
return (int)(0.5 + len * Math.sin(dir));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// ActionListener
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
Object source = e.getSource();
|
|
||||||
if (!isMap && source == timer) {
|
|
||||||
if (isLayoutActive) {
|
|
||||||
updateNodeLayout();
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (!isMap && source == lockedItem) {
|
|
||||||
if (popupNode != null) {
|
|
||||||
boolean wasFixed = popupNode.hasFixedLocation;
|
|
||||||
popupNode.hasFixedLocation = lockedItem.isSelected();
|
|
||||||
if (wasFixed && !popupNode.hasFixedLocation) {
|
|
||||||
server.removeConfig("collect.map." + popupNode.node.getID());
|
|
||||||
} else if (!wasFixed && popupNode.hasFixedLocation) {
|
|
||||||
server.setConfig("collect.map." + popupNode.node.getID(),
|
|
||||||
"" + popupNode.x + ',' + popupNode.y);
|
|
||||||
}
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (!isMap && source == layoutItem) {
|
|
||||||
isLayoutActive = layoutItem.isSelected();
|
|
||||||
|
|
||||||
} else if (!isMap && source == shakeItem) {
|
|
||||||
for(MapNode n : getNodeList()) {
|
|
||||||
if (!n.hasFixedLocation) {
|
|
||||||
n.x += Math.random() * 100 - 50;
|
|
||||||
n.y += Math.random() * 100 - 50;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (!isMap && source == configItem) {
|
|
||||||
if (configItem.isSelected()) {
|
|
||||||
configPanel.setSize(getPreferredSize());
|
|
||||||
configPanel.validate();
|
|
||||||
updateConfigLayout();
|
|
||||||
configPanel.setVisible(true);
|
|
||||||
} else {
|
|
||||||
configPanel.setVisible(false);
|
|
||||||
}
|
|
||||||
repaint();
|
|
||||||
|
|
||||||
} else if (source == showNetworkItem) {
|
|
||||||
hideNetwork = !showNetworkItem.isSelected();
|
|
||||||
repaint();
|
|
||||||
|
|
||||||
} else if (source == resetNetworkItem) {
|
|
||||||
for(MapNode n : getNodeList()) {
|
|
||||||
n.node.clearLinks();
|
|
||||||
}
|
|
||||||
repaint();
|
|
||||||
} else if (isMap && source == backgroundItem) {
|
|
||||||
showBackground = mapImage != null && backgroundItem.isSelected();
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateNodeLayout() {
|
|
||||||
MapNode[] nodes = getNodeList();
|
|
||||||
for (MapNode n : nodes) {
|
|
||||||
|
|
||||||
// Attract connected nodes
|
|
||||||
for(int i = 0, jn = n.node.getLinkCount(); i < jn; i++) {
|
|
||||||
Link link = n.node.getLink(i);
|
|
||||||
MapNode n2 = addMapNode(link.node);
|
|
||||||
double vx = n2.x - n.x;
|
|
||||||
double vy = n2.y - n.y;
|
|
||||||
double dist = Math.sqrt(vx * vx + vy * vy);
|
|
||||||
dist = dist == 0 ? 0.00001 : dist;
|
|
||||||
double etx = link.getETX() * etxFactor;
|
|
||||||
if (etx > 5) etx = 5;
|
|
||||||
double factor = (etx * layoutAttract - dist) / (dist * 3);
|
|
||||||
double dx = factor * vx;
|
|
||||||
double dy = factor * vy;
|
|
||||||
|
|
||||||
n2.dx += dx;
|
|
||||||
n2.dy += dy;
|
|
||||||
n.dx -= dx;
|
|
||||||
n.dy -= dy;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repel nodes that are too close
|
|
||||||
double dx = 0, dy = 0;
|
|
||||||
for (MapNode n2 : nodes) {
|
|
||||||
if (n == n2) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
double vx = n.x - n2.x;
|
|
||||||
double vy = n.y - n2.y;
|
|
||||||
double dist = vx * vx + vy * vy;
|
|
||||||
if (dist == 0) {
|
|
||||||
dx += Math.random() * 5;
|
|
||||||
dy += Math.random() * 5;
|
|
||||||
} else if (dist < layoutRepel * layoutRepel) {
|
|
||||||
dx += vx / dist;
|
|
||||||
dy += vy / dist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
double dist = dx * dx + dy * dy;
|
|
||||||
if (dist > 0) {
|
|
||||||
dist = Math.sqrt(dist) / 2;
|
|
||||||
n.dx += dx / dist;
|
|
||||||
n.dy += dy / dist;
|
|
||||||
}
|
|
||||||
|
|
||||||
n.dy += layoutGravity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the node positions
|
|
||||||
int width = getWidth();
|
|
||||||
int height = getHeight();
|
|
||||||
for(MapNode n : nodes) {
|
|
||||||
if (!n.hasFixedLocation && n != draggedNode) {
|
|
||||||
n.x += Math.max(-5, Math.min(5, n.dx));
|
|
||||||
n.y += Math.max(-5, Math.min(5, n.dy));
|
|
||||||
if (n.x < 0) {
|
|
||||||
n.x = 0;
|
|
||||||
} else if (n.x > width) {
|
|
||||||
n.x = width;
|
|
||||||
}
|
|
||||||
if (n.y < 0) {
|
|
||||||
n.y = 0;
|
|
||||||
} else if (n.y > height) {
|
|
||||||
n.y = height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n.dx /= 2;
|
|
||||||
n.dy /= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateConfigLayout() {
|
|
||||||
configPanel.setLocation(getWidth() - configPanel.getWidth() - 10,
|
|
||||||
getHeight() - configPanel.getHeight() - 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// Mouselistener
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
|
|
||||||
private MapNode getNodeAt(int mx, int my) {
|
|
||||||
for(MapNode n : getNodeList()) {
|
|
||||||
if (mx >= (n.x - delta)
|
|
||||||
&& mx <= (n.x + delta)
|
|
||||||
&& my >= (n.y - delta)
|
|
||||||
&& my <= (n.y + delta)) {
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mouseClicked(MouseEvent e) {
|
|
||||||
int mx = e.getX();
|
|
||||||
int my = e.getY();
|
|
||||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
|
||||||
MapNode node = getNodeAt(mx, my);
|
|
||||||
if (node != selectedNode) {
|
|
||||||
server.selectNodes(node == null ? null : new Node[] { node.node });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
showPopup(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mousePressed(MouseEvent e) {
|
|
||||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
|
||||||
MapNode aNode = getNodeAt(e.getX(), e.getY());
|
|
||||||
if (aNode != selectedNode) {
|
|
||||||
server.selectNodes(aNode != null ? new Node[] { aNode.node } : null);
|
|
||||||
}
|
|
||||||
draggedNode = aNode;
|
|
||||||
draggedTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
} else if (selectedNode != null) {
|
|
||||||
if (draggedTime < 0) {
|
|
||||||
setCursor(Cursor.getDefaultCursor());
|
|
||||||
draggedTime = 0;
|
|
||||||
}
|
|
||||||
selectedNode = draggedNode = null;
|
|
||||||
server.selectNodes(null);
|
|
||||||
}
|
|
||||||
showPopup(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mouseReleased(MouseEvent e) {
|
|
||||||
if (draggedNode != null && e.getButton() == MouseEvent.BUTTON1) {
|
|
||||||
if ((draggedTime > 0) &&
|
|
||||||
(System.currentTimeMillis() - draggedTime) < 300) {
|
|
||||||
// Do not drag if mouse is only moved during click
|
|
||||||
|
|
||||||
} else {
|
|
||||||
draggedNode.x = e.getX();
|
|
||||||
draggedNode.y = e.getY();
|
|
||||||
setCursor(Cursor.getDefaultCursor());
|
|
||||||
if (!isMap && draggedNode.hasFixedLocation) {
|
|
||||||
/* Update fixed location */
|
|
||||||
server.setConfig("collect.map." + draggedNode.node.getID(),
|
|
||||||
"" + draggedNode.x + ',' + draggedNode.y);
|
|
||||||
}
|
|
||||||
draggedTime = 0;
|
|
||||||
draggedNode = null;
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showPopup(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showPopup(MouseEvent e) {
|
|
||||||
if (e.isPopupTrigger()
|
|
||||||
&& (e.getModifiers() & (MouseEvent.SHIFT_MASK|MouseEvent.CTRL_MASK)) == 0) {
|
|
||||||
popupNode = getNodeAt(e.getX(), e.getY());
|
|
||||||
if (!isMap) {
|
|
||||||
lockedItem.setEnabled(popupNode != null);
|
|
||||||
lockedItem.setSelected(popupNode != null ? popupNode.hasFixedLocation : false);
|
|
||||||
}
|
|
||||||
popupMenu.show(this, e.getX(), e.getY());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mouseEntered(MouseEvent e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mouseExited(MouseEvent e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// MouseMotion
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
|
|
||||||
public void mouseDragged(MouseEvent e) {
|
|
||||||
if (draggedNode == null) {
|
|
||||||
// Do nothing
|
|
||||||
|
|
||||||
} else if (draggedTime > 0) {
|
|
||||||
if ((System.currentTimeMillis() - draggedTime) > 300) {
|
|
||||||
// No mouse click, time to drag the node
|
|
||||||
draggedTime = -1;
|
|
||||||
setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR));
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (VISUAL_DRAG /* && dragItem.isSelected() */) {
|
|
||||||
draggedNode.x = e.getX();
|
|
||||||
draggedNode.y = e.getY();
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void mouseMoved(MouseEvent e) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
// MapNode
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
|
|
||||||
private static class MapNode {
|
|
||||||
|
|
||||||
public final Node node;
|
|
||||||
public int x;
|
|
||||||
public int y;
|
|
||||||
public double dx;
|
|
||||||
public double dy;
|
|
||||||
public boolean hasFixedLocation;
|
|
||||||
public boolean isSelected;
|
|
||||||
public String message;
|
|
||||||
|
|
||||||
MapNode(MapPanel panel, Node node) {
|
|
||||||
this.node = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void paint(Graphics g, int x, int y) {
|
|
||||||
final int od = 3;
|
|
||||||
g.setColor(Color.black);
|
|
||||||
g.drawString(node.getID(), x + od * 2 + 3, y + 4);
|
|
||||||
if (hasFixedLocation) {
|
|
||||||
g.setColor(Color.red);
|
|
||||||
}
|
|
||||||
g.fillOval(x - od, y - od, od * 2 + 1, od * 2 + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end of inner class MapNode
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateConfig(Properties config) {
|
|
||||||
if (isMap) {
|
|
||||||
for (MapNode n : getNodeList()) {
|
|
||||||
config.put(n.node.getID(), "" + n.x + ',' + n.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,304 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* NodeControl
|
|
||||||
*
|
|
||||||
* Authors : Niclas Finne
|
|
||||||
* Created : 27 sep 2010
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect.gui;
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.FlowLayout;
|
|
||||||
import java.awt.GridBagConstraints;
|
|
||||||
import java.awt.GridBagLayout;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
|
|
||||||
import javax.swing.BorderFactory;
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JFormattedTextField;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JScrollPane;
|
|
||||||
import javax.swing.JSeparator;
|
|
||||||
import javax.swing.JTextPane;
|
|
||||||
import javax.swing.border.LineBorder;
|
|
||||||
|
|
||||||
import org.contikios.contiki.collect.CollectServer;
|
|
||||||
import org.contikios.contiki.collect.Node;
|
|
||||||
import org.contikios.contiki.collect.SensorData;
|
|
||||||
import org.contikios.contiki.collect.Visualizer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class NodeControl implements Visualizer {
|
|
||||||
|
|
||||||
private final static String SET_TIME_COMMAND = "time %TIME% | null";
|
|
||||||
|
|
||||||
private final CollectServer server;
|
|
||||||
private final String category;
|
|
||||||
private final JPanel panel;
|
|
||||||
private final JLabel statusLabel;
|
|
||||||
private final JSeparator statusSeparator;
|
|
||||||
|
|
||||||
public NodeControl(CollectServer server, String category) {
|
|
||||||
this.server = server;
|
|
||||||
this.category = category;
|
|
||||||
this.panel = new JPanel(new BorderLayout());
|
|
||||||
|
|
||||||
final JFormattedTextField intervalField = new JFormattedTextField(new Integer(60));
|
|
||||||
final JFormattedTextField randomField = new JFormattedTextField(new Integer(60));
|
|
||||||
final JFormattedTextField reportsField = new JFormattedTextField(new Integer(0));
|
|
||||||
final JFormattedTextField rexmitsField = new JFormattedTextField(new Integer(31));
|
|
||||||
statusLabel = new JLabel("", JLabel.CENTER);
|
|
||||||
statusLabel.setOpaque(true);
|
|
||||||
statusLabel.setBackground(Color.white);
|
|
||||||
statusLabel.setBorder(LineBorder.createBlackLineBorder());
|
|
||||||
statusLabel.setVisible(false);
|
|
||||||
statusSeparator = new JSeparator();
|
|
||||||
statusSeparator.setVisible(false);
|
|
||||||
|
|
||||||
JButton stopButton = createCommandButton("Send stop to nodes", "netcmd killall");
|
|
||||||
|
|
||||||
JButton sendButton = new JButton("Send command to nodes");
|
|
||||||
sendButton.addActionListener(new ActionListener() {
|
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
int interval = (Integer)intervalField.getValue();
|
|
||||||
int random = (Integer)randomField.getValue();
|
|
||||||
int reports = (Integer)reportsField.getValue();
|
|
||||||
int rexmits = (Integer)rexmitsField.getValue();
|
|
||||||
|
|
||||||
sendCommand("netcmd { repeat " + reports + " " + interval
|
|
||||||
+ " { randwait " + random + " collect-view-data | send " + rexmits + " } }");
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
JButton collectButton = createCommandButton("Start Collect",
|
|
||||||
"~K", "killall",
|
|
||||||
"mac 0", SET_TIME_COMMAND,
|
|
||||||
"collect | timestamp | binprint &");
|
|
||||||
JButton stopCollectButton = createCommandButton("Stop Collect", "~K", "killall");
|
|
||||||
|
|
||||||
JPanel controlPanel = new JPanel(new GridBagLayout());
|
|
||||||
|
|
||||||
GridBagConstraints c = new GridBagConstraints();
|
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
|
||||||
c.weightx = 0.5;
|
|
||||||
c.insets.left = c.insets.right = c.insets.bottom = 3;
|
|
||||||
c.anchor = GridBagConstraints.WEST;
|
|
||||||
c.gridy = 0;
|
|
||||||
|
|
||||||
c.gridwidth = 3;
|
|
||||||
controlPanel.add(statusLabel, c);
|
|
||||||
c.gridy++;
|
|
||||||
controlPanel.add(statusSeparator, c);
|
|
||||||
c.insets.top = 10;
|
|
||||||
|
|
||||||
c.gridy++;
|
|
||||||
c.gridwidth = 1;
|
|
||||||
controlPanel.add(new JLabel("Program Connected Nodes", JLabel.RIGHT), c);
|
|
||||||
c.gridwidth = 3;
|
|
||||||
c.fill = GridBagConstraints.NONE;
|
|
||||||
controlPanel.add(new JButton(server.getMoteProgramAction()), c);
|
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
|
||||||
|
|
||||||
c.gridy++;
|
|
||||||
c.gridwidth = 1;
|
|
||||||
controlPanel.add(new JLabel("Serial Connection", JLabel.RIGHT), c);
|
|
||||||
c.gridwidth = 3;
|
|
||||||
c.fill = GridBagConstraints.NONE;
|
|
||||||
controlPanel.add(new JButton(server.getConnectSerialAction()), c);
|
|
||||||
c.fill = GridBagConstraints.HORIZONTAL;
|
|
||||||
|
|
||||||
c.gridy++;
|
|
||||||
controlPanel.add(new JSeparator(), c);
|
|
||||||
|
|
||||||
c.gridy++;
|
|
||||||
c.gridwidth = 1;
|
|
||||||
controlPanel.add(new JLabel("Base Station Control", JLabel.RIGHT), c);
|
|
||||||
c.gridwidth = 2;
|
|
||||||
JPanel basePanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 5));
|
|
||||||
basePanel.add(collectButton);
|
|
||||||
basePanel.add(stopCollectButton);
|
|
||||||
c.insets.left -= 5;
|
|
||||||
controlPanel.add(basePanel, c);
|
|
||||||
c.insets.left += 5;
|
|
||||||
|
|
||||||
c.gridy++;
|
|
||||||
c.gridwidth = 3;
|
|
||||||
controlPanel.add(new JSeparator(), c);
|
|
||||||
|
|
||||||
c.gridy++;
|
|
||||||
JLabel label = new JLabel("Collect Settings", JLabel.CENTER);
|
|
||||||
controlPanel.add(label, c);
|
|
||||||
c.gridwidth = 1;
|
|
||||||
|
|
||||||
c.gridy++;
|
|
||||||
controlPanel.add(label = new JLabel("Report interval", JLabel.RIGHT), c);
|
|
||||||
label.setLabelFor(intervalField);
|
|
||||||
controlPanel.add(intervalField, c);
|
|
||||||
controlPanel.add(new JLabel("seconds"), c);
|
|
||||||
|
|
||||||
c.insets.top = 3;
|
|
||||||
c.gridy++;
|
|
||||||
controlPanel.add(label = new JLabel("Report randomness", JLabel.RIGHT), c);
|
|
||||||
label.setLabelFor(randomField);
|
|
||||||
controlPanel.add(randomField, c);
|
|
||||||
controlPanel.add(new JLabel("seconds"), c);
|
|
||||||
|
|
||||||
c.gridy++;
|
|
||||||
controlPanel.add(label = new JLabel("Hop-by-hop retransmissions", JLabel.RIGHT), c);
|
|
||||||
label.setLabelFor(rexmitsField);
|
|
||||||
controlPanel.add(rexmitsField, c);
|
|
||||||
controlPanel.add(new JLabel("retransmissions (0 - 31)"), c);
|
|
||||||
|
|
||||||
c.gridy++;
|
|
||||||
controlPanel.add(new JLabel("Number of reports", JLabel.RIGHT), c);
|
|
||||||
label.setLabelFor(reportsField);
|
|
||||||
controlPanel.add(reportsField, c);
|
|
||||||
controlPanel.add(new JLabel("(0 = report forever)"), c);
|
|
||||||
|
|
||||||
c.gridy++;
|
|
||||||
c.gridwidth = 3;
|
|
||||||
c.insets.bottom = 10;
|
|
||||||
JPanel nodePanel = new JPanel();
|
|
||||||
nodePanel.add(sendButton);
|
|
||||||
nodePanel.add(stopButton);
|
|
||||||
controlPanel.add(nodePanel, c);
|
|
||||||
|
|
||||||
c.gridy++;
|
|
||||||
controlPanel.add(new JSeparator(), c);
|
|
||||||
panel.add(controlPanel, BorderLayout.NORTH);
|
|
||||||
|
|
||||||
JTextPane helpPane = new JTextPane();
|
|
||||||
helpPane.setContentType("text/html");
|
|
||||||
helpPane.setEditable(false);
|
|
||||||
helpPane.setText("<html>" +
|
|
||||||
"<h3>Quick Startup Instructions</h3>" +
|
|
||||||
"<lu>" +
|
|
||||||
"<li> Connect nodes to USB. Press the <strong>Program Nodes...</strong> button." +
|
|
||||||
"<li> Disconnect all except one node. " +
|
|
||||||
"Press the <strong>Connect to Serial</strong> button." +
|
|
||||||
"<li> Press the <strong>Start Collect</strong> button." +
|
|
||||||
"<li> Press the <strong>Send command to nodes</strong> button." +
|
|
||||||
"</lu>" +
|
|
||||||
"</html>");
|
|
||||||
helpPane.setBackground(panel.getBackground());
|
|
||||||
JScrollPane helpScroll = new JScrollPane(helpPane,
|
|
||||||
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
|
|
||||||
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
|
||||||
helpScroll.setBorder(BorderFactory.createEmptyBorder(3, 10, 10, 10));
|
|
||||||
panel.add(helpScroll, BorderLayout.CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
private JButton createCommandButton(String name, final String... command) {
|
|
||||||
JButton button = new JButton(name);
|
|
||||||
button.addActionListener(new ActionListener() {
|
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
try {
|
|
||||||
// TODO Should use separate thread to send commands
|
|
||||||
panel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
|
||||||
for(int i = 0, n = command.length; i < n; i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
try {
|
|
||||||
// Do not send multiple commands too fast
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (InterruptedException e1) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String cmd = command[i];
|
|
||||||
if (cmd == SET_TIME_COMMAND) {
|
|
||||||
cmd = "time " + (System.currentTimeMillis() / 1000) + " | null";
|
|
||||||
}
|
|
||||||
if (!sendCommand(cmd)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
panel.setCursor(Cursor.getDefaultCursor());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
return button;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean sendCommand(String command) {
|
|
||||||
if (server.sendToNode(command)) {
|
|
||||||
setStatus("Sent command '" + command + "'", false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
setStatus("Failed to send command. No serial connection.", true);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setStatus(String text, boolean isWarning) {
|
|
||||||
statusLabel.setForeground(isWarning ? Color.red : Color.black);
|
|
||||||
statusLabel.setText(text);
|
|
||||||
statusLabel.setVisible(true);
|
|
||||||
statusSeparator.setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCategory() {
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTitle() {
|
|
||||||
return "Node Control";
|
|
||||||
}
|
|
||||||
|
|
||||||
public Component getPanel() {
|
|
||||||
return panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void nodesSelected(Node[] node) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void nodeAdded(Node node) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void nodeDataReceived(SensorData sensorData) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearNodeData() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,650 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2010, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* NodeInfoPanel
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 6 sep 2010
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect.gui;
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.Font;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import javax.swing.AbstractAction;
|
|
||||||
import javax.swing.JCheckBoxMenuItem;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JPopupMenu;
|
|
||||||
import javax.swing.JScrollPane;
|
|
||||||
import javax.swing.JTable;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import javax.swing.table.AbstractTableModel;
|
|
||||||
import javax.swing.table.DefaultTableCellRenderer;
|
|
||||||
import javax.swing.table.JTableHeader;
|
|
||||||
import javax.swing.table.TableCellRenderer;
|
|
||||||
import javax.swing.table.TableColumn;
|
|
||||||
import javax.swing.table.TableRowSorter;
|
|
||||||
|
|
||||||
import org.contikios.contiki.collect.CollectServer;
|
|
||||||
import org.contikios.contiki.collect.Configurable;
|
|
||||||
import org.contikios.contiki.collect.Node;
|
|
||||||
import org.contikios.contiki.collect.SensorData;
|
|
||||||
import org.contikios.contiki.collect.SensorInfo;
|
|
||||||
import org.contikios.contiki.collect.Visualizer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class NodeInfoPanel extends JPanel implements Visualizer, Configurable {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -1060893468047793431L;
|
|
||||||
|
|
||||||
private static Comparator<Number> NUMBER_COMPARATOR = new Comparator<Number>() {
|
|
||||||
|
|
||||||
public int compare(Number o1, Number o2) {
|
|
||||||
double v1 = o1.doubleValue();
|
|
||||||
double v2 = o2.doubleValue();
|
|
||||||
return (v1 < v2 ? -1 : (v1 == v2 ? 0 : 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
private final CollectServer server;
|
|
||||||
private final String category;
|
|
||||||
private final JTable table;
|
|
||||||
private final NodeModel nodeModel;
|
|
||||||
|
|
||||||
@SuppressWarnings("serial")
|
|
||||||
public NodeInfoPanel(CollectServer server, String category) {
|
|
||||||
super(new BorderLayout());
|
|
||||||
this.server = server;
|
|
||||||
this.category = category;
|
|
||||||
|
|
||||||
TableData[] columns = new TableData[] {
|
|
||||||
new TableData("Node", Node.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Received", "Packets Received", Number.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getPacketCount();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Dups", "Duplicate Packets Received", Number.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getDuplicateCount();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Lost", "Estimated Lost Packets", Number.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getEstimatedLostCount();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Hops", "Average Hops to Sink", Double.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getAverageValue(SensorData.HOPS);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Rtmetric", "Average Routing Metric", Double.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getAverageRtmetric();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("ETX", "Average ETX to Next Hop", Double.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getAverageBestNeighborETX();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Churn", "Next Hop Change Count", Number.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getNextHopChangeCount();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Beacon Interval", "Average Beacon Interval", Long.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return (long)(node.getSensorDataAggregator().getAverageValue(SensorData.BEACON_INTERVAL) * 1000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new TableData("Reboots", "Estimated Node Restart Count", Number.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getEstimatedRestarts();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Power
|
|
||||||
new TableData("CPU Power", "Average CPU Power Consumption", Double.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getCPUPower();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("LPM Power", "Average LPM Power Consumption", Double.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getLPMPower();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Listen Power", "Average Radio Listen Power Consumption", Double.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getListenPower();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Transmit Power", "Average Radio Transmit Power Consumption", Double.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getTransmitPower();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Power", "Average Power Consumption", Double.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getAveragePower();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("On-time", "Power Measure Time", Long.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getPowerMeasureTime();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
new TableData("Listen Duty Cycle", "Average Radio Listen Duty Cycle (%)", Double.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return 100 * node.getSensorDataAggregator().getAverageDutyCycle(SensorInfo.TIME_LISTEN);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Transmit Duty Cycle", "Average Radio Transmit Duty Cycle (%)", Double.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return 100 * node.getSensorDataAggregator().getAverageDutyCycle(SensorInfo.TIME_TRANSMIT);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Inter-packet times
|
|
||||||
new TableData("Avg Inter-packet Time", Long.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getAveragePeriod();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Min Inter-packet Time", Long.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getShortestPeriod();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TableData("Max Inter-packet Time", Long.class) {
|
|
||||||
public Object getValue(Node node) {
|
|
||||||
return node.getSensorDataAggregator().getLongestPeriod();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
nodeModel = new NodeModel(columns);
|
|
||||||
table = new JTable(nodeModel) {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private Font fontForAverage;
|
|
||||||
|
|
||||||
protected JTableHeader createDefaultTableHeader() {
|
|
||||||
return new JTableHeader(columnModel) {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
public String getToolTipText(MouseEvent e) {
|
|
||||||
int index = columnModel.getColumnIndexAtX(e.getX());
|
|
||||||
int modelIndex = index < 0 ? index : columnModel.getColumn(index).getModelIndex();
|
|
||||||
return modelIndex < 0 ? null : nodeModel.getColumnToolTip(modelIndex);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public Component prepareRenderer(TableCellRenderer renderer, int rowIndex, int vColIndex) {
|
|
||||||
Component c = super.prepareRenderer(renderer, rowIndex, vColIndex);
|
|
||||||
int row = convertRowIndexToModel(rowIndex);
|
|
||||||
if (row == nodeModel.getRowCount() - 1) {
|
|
||||||
if (fontForAverage == null) {
|
|
||||||
fontForAverage = c.getFont().deriveFont(Font.BOLD);
|
|
||||||
}
|
|
||||||
// Last line is average
|
|
||||||
c.setFont(fontForAverage);
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// Do not sort column when clicking between the columns (resizing)
|
|
||||||
TableRowSorter<NodeModel> sorter = new TableRowSorter<NodeModel>(nodeModel) {
|
|
||||||
public void toggleSortOrder(int column) {
|
|
||||||
if(table.getTableHeader().getCursor().getType() != Cursor.E_RESIZE_CURSOR) {
|
|
||||||
super.toggleSortOrder(column);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
for(int c = 0; c < columns.length; c++) {
|
|
||||||
if (columns[c].dataClass == Number.class) {
|
|
||||||
sorter.setComparator(c, NUMBER_COMPARATOR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
table.setRowSorter(sorter);
|
|
||||||
// Pack the column when double clicking between columns (resizing)
|
|
||||||
table.getTableHeader().addMouseListener(new MouseAdapter() {
|
|
||||||
public void mouseClicked(MouseEvent e) {
|
|
||||||
if(e.getClickCount() == 2 && SwingUtilities.isLeftMouseButton(e) &&
|
|
||||||
table.getTableHeader().getCursor().getType() == Cursor.E_RESIZE_CURSOR) {
|
|
||||||
int index = table.getColumnModel().getColumnIndexAtX(e.getX() - 3);
|
|
||||||
if (index >= 0) {
|
|
||||||
packColumn(table, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add right aligned renderer for node name
|
|
||||||
DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
|
|
||||||
renderer.setHorizontalAlignment(JLabel.RIGHT);
|
|
||||||
table.setDefaultRenderer(Node.class, renderer);
|
|
||||||
|
|
||||||
// Add renderer for time
|
|
||||||
renderer = new DefaultTableCellRenderer() {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
public void setValue(Object value) {
|
|
||||||
long time = (Long) value;
|
|
||||||
setText(time > 0 ? getTimeAsString(time) : null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
renderer.setHorizontalAlignment(JLabel.RIGHT);
|
|
||||||
table.setDefaultRenderer(Long.class, renderer);
|
|
||||||
|
|
||||||
// Add renderer for double
|
|
||||||
renderer = new DefaultTableCellRenderer() {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
public void setValue(Object value) {
|
|
||||||
if (value == null) {
|
|
||||||
setText(null);
|
|
||||||
} else {
|
|
||||||
double v = ((Number) value).doubleValue() + 0.0005;
|
|
||||||
int dec = ((int)(v * 1000)) % 1000;
|
|
||||||
setText((long)v + "." + (dec > 99 ? "" : "0") + (dec > 9 ? "" : "0") + dec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
renderer.setHorizontalAlignment(JLabel.RIGHT);
|
|
||||||
table.setDefaultRenderer(Double.class, renderer);
|
|
||||||
|
|
||||||
// Add renderer for mixed integers and doubles
|
|
||||||
renderer = new DefaultTableCellRenderer() {
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
public void setValue(Object value) {
|
|
||||||
if (value == null) {
|
|
||||||
setText(null);
|
|
||||||
} else if (value instanceof Integer) {
|
|
||||||
setText(value.toString());
|
|
||||||
} else {
|
|
||||||
double v = ((Number) value).doubleValue() + 0.0005;
|
|
||||||
int dec = ((int)(v * 1000)) % 1000;
|
|
||||||
setText((long)v + "." + (dec > 99 ? "" : "0") + (dec > 9 ? "" : "0") + dec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
renderer.setHorizontalAlignment(JLabel.RIGHT);
|
|
||||||
table.setDefaultRenderer(Number.class, renderer);
|
|
||||||
|
|
||||||
table.setFillsViewportHeight(true);
|
|
||||||
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
|
||||||
for (int i = 0, n = table.getColumnCount(); i < n; i++) {
|
|
||||||
packColumn(table, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
String savedColumnData = server.getConfig("collect.nodeinfo.table");
|
|
||||||
if (savedColumnData != null) {
|
|
||||||
String[] columnList = savedColumnData.split("[ ,]");
|
|
||||||
for(int i = 1; i < columns.length; i++) {
|
|
||||||
columns[i].setVisible(false);
|
|
||||||
}
|
|
||||||
for(int i = 0; i < columnList.length; i++) {
|
|
||||||
int c = Integer.parseInt(columnList[i]);
|
|
||||||
int index = table.convertColumnIndexToView(c);
|
|
||||||
if (index >= 0) {
|
|
||||||
table.getColumnModel().moveColumn(index, i);
|
|
||||||
}
|
|
||||||
columns[c].setVisible(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JPopupMenu popupMenu = new JPopupMenu();
|
|
||||||
// The first column (the node name) should always be visible.
|
|
||||||
for(int i = 1; i < columns.length; i++) {
|
|
||||||
popupMenu.add(new JCheckBoxMenuItem(columns[i].init(table, i)));
|
|
||||||
}
|
|
||||||
table.setComponentPopupMenu(popupMenu);
|
|
||||||
add(new JScrollPane(table), BorderLayout.CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateConfig(Properties config) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for(int i = 0, n = table.getColumnCount(); i < n; i++) {
|
|
||||||
int index = table.convertColumnIndexToModel(i);
|
|
||||||
if (index >= 0) {
|
|
||||||
if (sb.length() > 0) {
|
|
||||||
sb.append(',');
|
|
||||||
}
|
|
||||||
sb.append(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config.setProperty("collect.nodeinfo.table", sb.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getPanel() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCategory() {
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTitle() {
|
|
||||||
return "Node Info";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeAdded(Node node) {
|
|
||||||
nodeModel.setNodes(server.getNodes());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeDataReceived(SensorData sensorData) {
|
|
||||||
nodeModel.updateNode(sensorData.getNode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearNodeData() {
|
|
||||||
nodeModel.setNodes(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodesSelected(Node[] nodes) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setVisible(boolean visible) {
|
|
||||||
nodeModel.setNodes(visible ? server.getNodes() : null);
|
|
||||||
super.setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getTimeAsString(long time) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
time /= 1000;
|
|
||||||
if (time > 24 * 60 * 60) {
|
|
||||||
long days = time / (24 * 60 * 60);
|
|
||||||
sb.append(days).append(days > 1 ? " days, " : " day, ");
|
|
||||||
time -= days * 24 * 60 * 60;
|
|
||||||
}
|
|
||||||
if (time > 60 * 60) {
|
|
||||||
long hours = time / (60 * 60);
|
|
||||||
sb.append(hours).append(hours > 1 ? " hours, " : " hour, ");
|
|
||||||
time -= hours * 60 * 60;
|
|
||||||
}
|
|
||||||
long sec = time % 60;
|
|
||||||
sb.append(time / 60).append(" min, ");
|
|
||||||
if (sec < 10) {
|
|
||||||
sb.append('0');
|
|
||||||
}
|
|
||||||
sb.append(sec).append(" sec");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void packColumn(JTable table, int columnIndex) {
|
|
||||||
TableColumn tableColumn = table.getColumnModel().getColumn(columnIndex);
|
|
||||||
Object value = tableColumn.getHeaderValue();
|
|
||||||
TableCellRenderer columnRenderer = tableColumn.getHeaderRenderer();
|
|
||||||
if (columnRenderer == null) {
|
|
||||||
columnRenderer = table.getTableHeader().getDefaultRenderer();
|
|
||||||
}
|
|
||||||
Component c = columnRenderer.getTableCellRendererComponent(table, value, false, false, -1, columnIndex);
|
|
||||||
int width = c.getPreferredSize().width + 6;
|
|
||||||
int intercellSpacing = table.getIntercellSpacing().width;
|
|
||||||
for(int i = 0, n = table.getRowCount(); i < n; i++) {
|
|
||||||
TableCellRenderer cellRenderer = table.getCellRenderer(i, columnIndex);
|
|
||||||
value = table.getValueAt(i, columnIndex);
|
|
||||||
c = cellRenderer.getTableCellRendererComponent(table, value, false, false, i, columnIndex);
|
|
||||||
int w = c.getPreferredSize().width + intercellSpacing + 2;
|
|
||||||
if (w > width) {
|
|
||||||
width = w;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
table.getTableHeader().setResizingColumn(tableColumn);
|
|
||||||
tableColumn.setWidth(width);
|
|
||||||
tableColumn.setPreferredWidth(width);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class NodeModel extends AbstractTableModel {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1692207305977527004L;
|
|
||||||
|
|
||||||
private final TableData[] columns;
|
|
||||||
private Node[] nodes;
|
|
||||||
|
|
||||||
public NodeModel(TableData[] columns) {
|
|
||||||
this.columns = columns;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void recalculateAverage() {
|
|
||||||
for(TableData td : columns) {
|
|
||||||
td.clearAverageCache();
|
|
||||||
}
|
|
||||||
int row = getRowCount() - 1;
|
|
||||||
fireTableRowsUpdated(row, row);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getValueAt(int row, int col) {
|
|
||||||
int count = nodes == null ? 0 : nodes.length;
|
|
||||||
if (row == count) {
|
|
||||||
return columns[col].getAverageValue(nodes);
|
|
||||||
}
|
|
||||||
return columns[col].getValue(nodes[row]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<?> getColumnClass(int col) {
|
|
||||||
return columns[col].dataClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getColumnName(int col) {
|
|
||||||
return columns[col].name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getColumnToolTip(int col) {
|
|
||||||
Object v = columns[col].getValue(TableData.SHORT_DESCRIPTION);
|
|
||||||
return v == null ? null : v.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getColumnCount() {
|
|
||||||
return columns.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRowCount() {
|
|
||||||
return (nodes == null ? 0 : nodes.length) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNodes(Node[] nodes) {
|
|
||||||
if (this.nodes != null && this.nodes.length > 0) {
|
|
||||||
fireTableRowsDeleted(0, this.nodes.length - 1);
|
|
||||||
}
|
|
||||||
this.nodes = nodes;
|
|
||||||
if (this.nodes != null && this.nodes.length > 0) {
|
|
||||||
fireTableRowsInserted(0, this.nodes.length - 1);
|
|
||||||
}
|
|
||||||
recalculateAverage();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateNode(Node node) {
|
|
||||||
if (this.nodes != null) {
|
|
||||||
for(int row = 0; row < this.nodes.length; row++) {
|
|
||||||
if (this.nodes[row] == node) {
|
|
||||||
fireTableRowsUpdated(row, row);
|
|
||||||
recalculateAverage();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static abstract class TableData extends AbstractAction {
|
|
||||||
private static final long serialVersionUID = -3045755073722516926L;
|
|
||||||
|
|
||||||
private final static Node AVERAGE_NODE = new Node("99999999.9", "Avg");
|
|
||||||
|
|
||||||
public final String name;
|
|
||||||
public final Class<?> dataClass;
|
|
||||||
|
|
||||||
private JTable table;
|
|
||||||
private TableColumn tableColumn;
|
|
||||||
private int modelIndex = -1;
|
|
||||||
|
|
||||||
private Object averageCache;
|
|
||||||
|
|
||||||
protected TableData(String name, Class<?> dataClass) {
|
|
||||||
this(name, name, dataClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected TableData(String name, String description, Class<?> dataClass) {
|
|
||||||
super(name);
|
|
||||||
this.name = name;
|
|
||||||
this.dataClass = dataClass;
|
|
||||||
putValue(SHORT_DESCRIPTION, description);
|
|
||||||
setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
TableData init(JTable table, int modelIndex) {
|
|
||||||
this.table = table;
|
|
||||||
this.modelIndex = modelIndex;
|
|
||||||
if (!isVisible()) {
|
|
||||||
// The column should initially be hidden
|
|
||||||
setColumnVisible(false);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVisible() {
|
|
||||||
return Boolean.TRUE.equals(getValue(SELECTED_KEY));
|
|
||||||
}
|
|
||||||
|
|
||||||
public TableData setVisible(boolean isVisible) {
|
|
||||||
putValue(SELECTED_KEY, isVisible);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent event) {
|
|
||||||
if (modelIndex >= 0) {
|
|
||||||
setColumnVisible(isVisible());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setColumnVisible(boolean isVisible) {
|
|
||||||
if (isVisible) {
|
|
||||||
if (tableColumn != null) {
|
|
||||||
int count = table.getColumnCount();
|
|
||||||
table.addColumn(tableColumn);
|
|
||||||
tableColumn = null;
|
|
||||||
packColumn(table, count);
|
|
||||||
|
|
||||||
int newIndex = 0;
|
|
||||||
for(int i = 0; i < modelIndex; i++) {
|
|
||||||
if (table.convertColumnIndexToView(i) >= 0) {
|
|
||||||
// The new column should be after this visible column
|
|
||||||
newIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (newIndex < count) {
|
|
||||||
table.getColumnModel().moveColumn(count, newIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int columnIndex = table.convertColumnIndexToView(modelIndex);
|
|
||||||
if (columnIndex >= 0 ) {
|
|
||||||
tableColumn = table.getColumnModel().getColumn(columnIndex);
|
|
||||||
table.removeColumn(tableColumn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Object getAverageValue(Node[] nodes) {
|
|
||||||
Object tmp = averageCache;
|
|
||||||
if (tmp != null) {
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataClass == Long.class || dataClass == Double.class
|
|
||||||
|| dataClass == Number.class) {
|
|
||||||
double average = 0.0;
|
|
||||||
if (nodes != null && nodes.length > 0) {
|
|
||||||
int count = 0;
|
|
||||||
for(Node node : nodes) {
|
|
||||||
if (node.getSensorDataAggregator().getDataCount() > 0) {
|
|
||||||
average += ((Number) getValue(node)).doubleValue();
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count > 0) {
|
|
||||||
average = average / count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dataClass == Long.class) {
|
|
||||||
tmp = (long) (average + 0.5);
|
|
||||||
} else {
|
|
||||||
tmp = average;
|
|
||||||
}
|
|
||||||
} else if (dataClass == Node.class) {
|
|
||||||
tmp = AVERAGE_NODE;
|
|
||||||
}
|
|
||||||
averageCache = tmp;
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void clearAverageCache() {
|
|
||||||
averageCache = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract Object getValue(Node node);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,224 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* SerialConsole
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 4 jul 2008
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect.gui;
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.KeyAdapter;
|
|
||||||
import java.awt.event.KeyEvent;
|
|
||||||
import javax.swing.JMenuItem;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JPopupMenu;
|
|
||||||
import javax.swing.JScrollPane;
|
|
||||||
import javax.swing.JTextArea;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import org.contikios.contiki.collect.CollectServer;
|
|
||||||
import org.contikios.contiki.collect.Node;
|
|
||||||
import org.contikios.contiki.collect.SensorData;
|
|
||||||
import org.contikios.contiki.collect.Visualizer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class SerialConsole implements Visualizer {
|
|
||||||
|
|
||||||
private final CollectServer server;
|
|
||||||
private final String category;
|
|
||||||
private JPanel panel;
|
|
||||||
private JTextArea logArea;
|
|
||||||
private JTextField commandField;
|
|
||||||
private String[] history = new String[50];
|
|
||||||
private int historyPos = 0;
|
|
||||||
private int historyCount = 0;
|
|
||||||
|
|
||||||
public SerialConsole(CollectServer server, String category) {
|
|
||||||
this.server = server;
|
|
||||||
this.category = category;
|
|
||||||
panel = new JPanel(new BorderLayout());
|
|
||||||
logArea = new JTextArea(4, 30);
|
|
||||||
logArea.setEditable(false);
|
|
||||||
panel.add(new JScrollPane(logArea), BorderLayout.CENTER);
|
|
||||||
|
|
||||||
JPopupMenu popupMenu = new JPopupMenu();
|
|
||||||
JMenuItem clearItem = new JMenuItem("Clear");
|
|
||||||
clearItem.addActionListener(new ActionListener() {
|
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
logArea.setText("");
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
popupMenu.add(clearItem);
|
|
||||||
logArea.setComponentPopupMenu(popupMenu);
|
|
||||||
|
|
||||||
commandField = new JTextField();
|
|
||||||
commandField.addActionListener(new ActionListener() {
|
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
String command = trim(commandField.getText());
|
|
||||||
if (command != null) {
|
|
||||||
try {
|
|
||||||
int previous = historyCount - 1;
|
|
||||||
if (previous < 0) previous += history.length;
|
|
||||||
if (!command.equals(history[previous])) {
|
|
||||||
history[historyCount] = command;
|
|
||||||
historyCount = (historyCount + 1) % history.length;
|
|
||||||
}
|
|
||||||
historyPos = historyCount;
|
|
||||||
if (SerialConsole.this.server.sendToNode(command)) {
|
|
||||||
commandField.setText("");
|
|
||||||
} else {
|
|
||||||
addSerialData("*** failed to send command ***");
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
System.err.println("could not send '" + command + "':");
|
|
||||||
ex.printStackTrace();
|
|
||||||
JOptionPane.showMessageDialog(panel,
|
|
||||||
"could not send '" + command + "':\n"
|
|
||||||
+ ex, "ERROR",
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
commandField.getToolkit().beep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
commandField.addKeyListener(new KeyAdapter() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void keyPressed(KeyEvent e) {
|
|
||||||
switch (e.getKeyCode()) {
|
|
||||||
case KeyEvent.VK_UP: {
|
|
||||||
int nextPos = (historyPos + history.length - 1) % history.length;
|
|
||||||
if (nextPos == historyCount || history[nextPos] == null) {
|
|
||||||
commandField.getToolkit().beep();
|
|
||||||
} else {
|
|
||||||
String cmd = trim(commandField.getText());
|
|
||||||
if (cmd != null) {
|
|
||||||
history[historyPos] = cmd;
|
|
||||||
}
|
|
||||||
historyPos = nextPos;
|
|
||||||
commandField.setText(history[historyPos]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case KeyEvent.VK_DOWN: {
|
|
||||||
int nextPos = (historyPos + 1) % history.length;
|
|
||||||
if (nextPos == historyCount) {
|
|
||||||
historyPos = nextPos;
|
|
||||||
commandField.setText("");
|
|
||||||
} else if (historyPos == historyCount || history[nextPos] == null) {
|
|
||||||
commandField.getToolkit().beep();
|
|
||||||
} else {
|
|
||||||
String cmd = trim(commandField.getText());
|
|
||||||
if (cmd != null) {
|
|
||||||
history[historyPos] = cmd;
|
|
||||||
}
|
|
||||||
historyPos = nextPos;
|
|
||||||
commandField.setText(history[historyPos]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
panel.add(commandField, BorderLayout.SOUTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getPanel() {
|
|
||||||
return panel;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCategory() {
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTitle() {
|
|
||||||
return "Serial Console";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeAdded(Node node) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeDataReceived(SensorData sensorData) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearNodeData() {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodesSelected(Node[] node) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addSerialData(final String text) {
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
String current = logArea.getText();
|
|
||||||
int len = current.length();
|
|
||||||
if (len > 4096) {
|
|
||||||
current = current.substring(len - 4096);
|
|
||||||
}
|
|
||||||
current = len > 0 ? (current + '\n' + text) : text;
|
|
||||||
logArea.setText(current);
|
|
||||||
logArea.setCaretPosition(current.length());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private String trim(String text) {
|
|
||||||
return (text != null) && ((text = text.trim()).length() > 0) ? text : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,328 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2008, Swedish Institute of Computer Science.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions
|
|
||||||
* are met:
|
|
||||||
* 1. Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* 2. Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* 3. Neither the name of the Institute nor the names of its contributors
|
|
||||||
* may be used to endorse or promote products derived from this software
|
|
||||||
* without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
|
|
||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
|
|
||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
||||||
* SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* -----------------------------------------------------------------
|
|
||||||
*
|
|
||||||
* TimeChartPanel
|
|
||||||
*
|
|
||||||
* Authors : Joakim Eriksson, Niclas Finne
|
|
||||||
* Created : 3 jul 2008
|
|
||||||
* Updated : $Date: 2010/11/03 14:53:05 $
|
|
||||||
* $Revision: 1.1 $
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.contikios.contiki.collect.gui;
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.util.Date;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import org.jfree.chart.ChartFactory;
|
|
||||||
import org.jfree.chart.ChartPanel;
|
|
||||||
import org.jfree.chart.JFreeChart;
|
|
||||||
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
|
|
||||||
import org.jfree.data.time.Second;
|
|
||||||
import org.jfree.data.time.TimeSeries;
|
|
||||||
import org.jfree.data.time.TimeSeriesCollection;
|
|
||||||
import org.contikios.contiki.collect.CollectServer;
|
|
||||||
import org.contikios.contiki.collect.Node;
|
|
||||||
import org.contikios.contiki.collect.SensorData;
|
|
||||||
import org.contikios.contiki.collect.Visualizer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public abstract class TimeChartPanel extends JPanel implements Visualizer {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = -607864439709540641L;
|
|
||||||
|
|
||||||
protected final CollectServer server;
|
|
||||||
protected final String category;
|
|
||||||
protected final String title;
|
|
||||||
protected final TimeSeriesCollection timeSeries;
|
|
||||||
protected final JFreeChart chart;
|
|
||||||
protected final ChartPanel chartPanel;
|
|
||||||
|
|
||||||
private Node[] selectedNodes;
|
|
||||||
|
|
||||||
private double minValue;
|
|
||||||
private double maxValue;
|
|
||||||
private int rangeTick = 0;
|
|
||||||
private boolean hasGlobalRange;
|
|
||||||
private int maxItemCount;
|
|
||||||
|
|
||||||
public TimeChartPanel(CollectServer server, String category, String title,
|
|
||||||
String chartTitle, String timeAxisLabel, String valueAxisLabel) {
|
|
||||||
super(new BorderLayout());
|
|
||||||
this.server = server;
|
|
||||||
this.category = category;
|
|
||||||
this.title = title;
|
|
||||||
this.timeSeries = new TimeSeriesCollection();
|
|
||||||
this.chart = ChartFactory.createTimeSeriesChart(
|
|
||||||
chartTitle, timeAxisLabel, valueAxisLabel, timeSeries,
|
|
||||||
true, true, false
|
|
||||||
);
|
|
||||||
this.chartPanel = new ChartPanel(chart);
|
|
||||||
this.chartPanel.setPreferredSize(new Dimension(500, 270));
|
|
||||||
setBaseShapeVisible(true);
|
|
||||||
setMaxItemCount(server.getDefaultMaxItemCount());
|
|
||||||
add(chartPanel, BorderLayout.CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCategory() {
|
|
||||||
return category;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTitle() {
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getPanel() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeAdded(Node node) {
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodesSelected(Node[] nodes) {
|
|
||||||
if (this.selectedNodes != nodes) {
|
|
||||||
this.selectedNodes = nodes;
|
|
||||||
if (isVisible()) {
|
|
||||||
updateCharts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void nodeDataReceived(SensorData data) {
|
|
||||||
if (hasGlobalRange) {
|
|
||||||
boolean update = false;
|
|
||||||
if (minValue > maxValue) {
|
|
||||||
update = true;
|
|
||||||
} else {
|
|
||||||
double value = getSensorDataValue(data);
|
|
||||||
if (value < minValue) {
|
|
||||||
minValue = value;
|
|
||||||
update = true;
|
|
||||||
}
|
|
||||||
if (value > maxValue) {
|
|
||||||
maxValue = value;
|
|
||||||
update = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (update && isVisible()) {
|
|
||||||
updateGlobalRange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isVisible() && selectedNodes != null && selectedNodes.length == timeSeries.getSeriesCount()) {
|
|
||||||
Node node = data.getNode();
|
|
||||||
for (int i = 0, n = selectedNodes.length; i < n; i++) {
|
|
||||||
if (node == selectedNodes[i]) {
|
|
||||||
TimeSeries series = timeSeries.getSeries(i);
|
|
||||||
int groupSize = getGroupSize(node);
|
|
||||||
if (groupSize > 1) {
|
|
||||||
series.clear();
|
|
||||||
updateSeries(series, node, groupSize);
|
|
||||||
} else {
|
|
||||||
series.addOrUpdate(new Second(new Date(data.getNodeTime())), getSensorDataValue(data));
|
|
||||||
}
|
|
||||||
chartPanel.repaint();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearNodeData() {
|
|
||||||
if (isVisible()) {
|
|
||||||
updateCharts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateCharts() {
|
|
||||||
timeSeries.removeAllSeries();
|
|
||||||
if (this.selectedNodes != null) {
|
|
||||||
for(Node node: this.selectedNodes) {
|
|
||||||
TimeSeries series = new TimeSeries(node.getName(), Second.class);
|
|
||||||
// Reduce the number of items by grouping them and use the average for each group
|
|
||||||
int groupSize = getGroupSize(node);
|
|
||||||
if (groupSize > 1) {
|
|
||||||
updateSeries(series, node, groupSize);
|
|
||||||
} else {
|
|
||||||
for (int i = 0, n = node.getSensorDataCount(); i < n; i++) {
|
|
||||||
SensorData data = node.getSensorData(i);
|
|
||||||
series.addOrUpdate(new Second(new Date(data.getNodeTime())), getSensorDataValue(data));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
timeSeries.addSeries(series);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getGroupSize(Node node) {
|
|
||||||
if (maxItemCount > 0) {
|
|
||||||
int sensorDataCount = node.getSensorDataCount();
|
|
||||||
if (sensorDataCount > maxItemCount) {
|
|
||||||
int groupSize = sensorDataCount / maxItemCount;
|
|
||||||
if (sensorDataCount / groupSize >= maxItemCount) {
|
|
||||||
groupSize++;
|
|
||||||
}
|
|
||||||
return groupSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateSeries(TimeSeries series, Node node, int groupSize) {
|
|
||||||
for (int i = 0, n = node.getSensorDataCount(); i < n; i += groupSize) {
|
|
||||||
double value = 0.0;
|
|
||||||
long time = 0L;
|
|
||||||
for (int j = 0; j < groupSize; j++) {
|
|
||||||
SensorData data = node.getSensorData(i);
|
|
||||||
value += getSensorDataValue(data);
|
|
||||||
time += data.getNodeTime() / 1000L;
|
|
||||||
}
|
|
||||||
series.addOrUpdate(new Second(new Date((time / groupSize) * 1000L)), value / groupSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getBaseShapeVisible() {
|
|
||||||
return ((XYLineAndShapeRenderer)this.chart.getXYPlot().getRenderer()).getBaseShapesVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBaseShapeVisible(boolean visible) {
|
|
||||||
((XYLineAndShapeRenderer)this.chart.getXYPlot().getRenderer()).setBaseShapesVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRangeTick() {
|
|
||||||
return rangeTick;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRangeTick(int rangeTick) {
|
|
||||||
this.rangeTick = rangeTick;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getRangeMinimumSize() {
|
|
||||||
return chart.getXYPlot().getRangeAxis().getAutoRangeMinimumSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRangeMinimumSize(double size) {
|
|
||||||
chart.getXYPlot().getRangeAxis().setAutoRangeMinimumSize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasGlobalRange() {
|
|
||||||
return hasGlobalRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGlobalRange(boolean hasGlobalRange) {
|
|
||||||
if (this.hasGlobalRange != hasGlobalRange) {
|
|
||||||
this.hasGlobalRange = hasGlobalRange;
|
|
||||||
if (hasGlobalRange) {
|
|
||||||
minValue = Double.MAX_VALUE;
|
|
||||||
maxValue = Double.MIN_NORMAL;
|
|
||||||
if (isVisible()) {
|
|
||||||
updateGlobalRange();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
chart.getXYPlot().getRangeAxis().setAutoRange(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateGlobalRange() {
|
|
||||||
if (hasGlobalRange) {
|
|
||||||
if (minValue > maxValue) {
|
|
||||||
for (int i = 0, n = server.getSensorDataCount(); i < n; i++) {
|
|
||||||
double value = getSensorDataValue(server.getSensorData(i));
|
|
||||||
if (value < minValue) minValue = value;
|
|
||||||
if (value > maxValue) maxValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (minValue < maxValue) {
|
|
||||||
double minSize = getRangeMinimumSize();
|
|
||||||
double min = minValue;
|
|
||||||
double max = maxValue;
|
|
||||||
if (max - min < minSize) {
|
|
||||||
double d = (minSize - (max - min)) / 2;
|
|
||||||
min -= d;
|
|
||||||
max += d;
|
|
||||||
}
|
|
||||||
if (rangeTick > 0) {
|
|
||||||
min = ((int) (min - rangeTick / 2) / rangeTick) * rangeTick;
|
|
||||||
// max = ((int) (max + rangeTick / 2) / rangeTick) * rangeTick;
|
|
||||||
}
|
|
||||||
chart.getXYPlot().getRangeAxis().setRange(min, max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the maximal number of chart items to display for each node.
|
|
||||||
*
|
|
||||||
* @return the maximal number of chart items to display for each node or <code>0</code>
|
|
||||||
* for unlimited number of chart items.
|
|
||||||
*/
|
|
||||||
public int getMaxItemCount() {
|
|
||||||
return maxItemCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the maximal number of chart items to display for each node. Items will be
|
|
||||||
* grouped and replaced by the average value when needed.
|
|
||||||
*
|
|
||||||
* @param maxItemCount - the maximal number of chart items to display for each node or
|
|
||||||
* <code>0</code> for unlimited number (default)
|
|
||||||
*/
|
|
||||||
public void setMaxItemCount(int maxItemCount) {
|
|
||||||
this.maxItemCount = maxItemCount;
|
|
||||||
if (isVisible()) {
|
|
||||||
updateCharts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setVisible(boolean visible) {
|
|
||||||
if (visible) {
|
|
||||||
updateGlobalRange();
|
|
||||||
updateCharts();
|
|
||||||
} else {
|
|
||||||
timeSeries.removeAllSeries();
|
|
||||||
}
|
|
||||||
super.setVisible(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract double getSensorDataValue(SensorData data);
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue