contiki/tools/cooja/java/se/sics/cooja/radiomediums/DirectedGraphMedium.java

477 lines
14 KiB
Java

/*
* Copyright (c) 2009, 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.
*
* $Id: DirectedGraphMedium.java,v 1.4 2009/11/25 15:44:04 fros4943 Exp $
*/
package se.sics.cooja.radiomediums;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Random;
import java.util.Vector;
import org.apache.log4j.Logger;
import org.jdom.Element;
import se.sics.cooja.ClassDescription;
import se.sics.cooja.Mote;
import se.sics.cooja.RadioConnection;
import se.sics.cooja.Simulation;
import se.sics.cooja.interfaces.Radio;
import se.sics.cooja.plugins.DGRMConfigurator;
/**
* Directed Graph Radio Medium.
*
* Can be used both stand-alone as a radio medium, and
* as a basis for other radio medium implementations.
*
* The stand-alone radio medium supports propagation delays and
* and single-value per-link transmission success ratio.
*
* @see UDGM
* @author Fredrik Osterlind
*/
@ClassDescription("Directed Graph Radio Medium (DGRM)")
public class DirectedGraphMedium extends AbstractRadioMedium {
private static Logger logger = Logger.getLogger(DirectedGraphMedium.class);
private Simulation simulation;
private Random random;
private ArrayList<Edge> edges = new ArrayList<Edge>();
private boolean edgesDirty = true;
/* Used for optimizing lookup time */
private Hashtable<Radio,DestinationRadio[]> edgesTable = new Hashtable<Radio,DestinationRadio[]>();
public DirectedGraphMedium() {
/* Do not initialize radio medium: use only for hash table */
super(null);
}
public DirectedGraphMedium(Simulation simulation) {
super(simulation);
this.simulation = simulation;
random = simulation.getRandomGenerator();
requestEdgeAnalysis();
/* Register plugin.
* TODO Should be unregistered when radio medium is removed */
simulation.getGUI().registerTemporaryPlugin(DGRMConfigurator.class);
}
public void addEdge(Edge e) {
edges.add(e);
requestEdgeAnalysis();
((AbstractRadioMedium.RadioMediumObservable)
this.getRadioMediumObservable()).setRadioMediumChangedAndNotify();
}
public void removeEdge(Edge edge) {
if (!edges.contains(edge)) {
logger.fatal("Cannot remove edge: " + edge);
return;
}
edges.remove(edge);
requestEdgeAnalysis();
((AbstractRadioMedium.RadioMediumObservable)
this.getRadioMediumObservable()).setRadioMediumChangedAndNotify();
}
public void clearEdges() {
edges.clear();
requestEdgeAnalysis();
((AbstractRadioMedium.RadioMediumObservable)
this.getRadioMediumObservable()).setRadioMediumChangedAndNotify();
}
public Edge[] getEdges() {
return edges.toArray(new Edge[0]);
}
/**
* Signal that the configuration changed, and needs to be re-analyzed
* before used.
*/
public void requestEdgeAnalysis() {
edgesDirty = true;
}
public boolean needsEdgeAnalysis() {
return edgesDirty;
}
public void registerRadioInterface(Radio radio, Simulation sim) {
super.registerRadioInterface(radio, sim);
for (Edge edge: edges) {
if (edge.delayedLoadConfig == null) {
continue;
}
/* Try to configure edge now */
if (edge.setConfigXML(edge.delayedLoadConfig, sim)) {
edge.delayedLoadConfig = null;
}
}
requestEdgeAnalysis();
}
public void unregisterRadioInterface(Radio radio, Simulation sim) {
super.unregisterRadioInterface(radio, sim);
if (radio == null) {
return;
}
for (Edge edge: getEdges()) {
if (edge.source == radio || edge.superDest.radio == radio) {
removeEdge(edge);
}
}
requestEdgeAnalysis();
}
public static class DestinationRadio {
public Radio radio; /* destination radio */
public boolean toAll; /* to all destinations */
public DestinationRadio(Radio dest) {
this.radio = dest;
toAll = (radio == null);
}
public String toString() {
return radio.getMote().toString();
}
protected Object clone() {
return new DestinationRadio(radio);
}
}
public static class DGRMDestinationRadio extends DestinationRadio {
public double ratio; /* Link success ratio (per packet). */
public long delay; /* EXPERIMENTAL: Propagation delay (us). */
public DGRMDestinationRadio(Radio dest, double ratio, long delay) {
super(dest);
this.ratio = ratio;
this.delay = delay;
}
protected Object clone() {
return new DGRMDestinationRadio(radio, ratio, delay);
}
}
/**
* Generates hash table using current edges for efficient lookup.
*/
protected void analyzeEdges() {
Hashtable<Radio,ArrayList<DestinationRadio>> listTable =
new Hashtable<Radio,ArrayList<DestinationRadio>>();
/* Fill edge hash table with all edges */
for (Edge edge: edges) {
if (edge.source == null) {
/* XXX Wait until edge configuration has been loaded */
logger.warn("DGRM edges not loaded");
return;
}
ArrayList<DestinationRadio> destRadios;
if (!listTable.containsKey(edge.source)) {
/* Create new source */
destRadios = new ArrayList<DestinationRadio>();
} else {
/* Extend source radio with another destination */
destRadios = listTable.get(edge.source);
}
/* Explode special rule: to all radios */
if (edge.superDest.toAll) {
for (Radio r: getRegisteredRadios()) {
if (edge.source == r) {
continue;
}
DestinationRadio d = (DestinationRadio) edge.superDest.clone();
d.radio = r;
d.toAll = false;
destRadios.add(d);
}
} else {
destRadios.add(edge.superDest);
}
listTable.put(edge.source, destRadios);
}
/* Convert to arrays */
Hashtable<Radio,DestinationRadio[]> arrTable =
new Hashtable<Radio,DestinationRadio[]>();
Enumeration<Radio> sources = listTable.keys();
while (sources.hasMoreElements()) {
Radio source = sources.nextElement();
DestinationRadio[] arr =
listTable.get(source).toArray(new DestinationRadio[0]);
arrTable.put(source, arr);
}
this.edgesTable = arrTable;
edgesDirty = false;
}
/**
* Returns all potential destination radios, i.e. all radios "within reach".
* Does not consider radio channels, transmission success ratios etc.
*
* @param source Source radio
* @return All potential destination radios
*/
public DestinationRadio[] getPotentialDestinations(Radio source) {
if (edgesDirty) {
analyzeEdges();
}
return edgesTable.get(source);
}
public RadioConnection createConnections(Radio source) {
if (edgesDirty) {
analyzeEdges();
}
if (edgesDirty) {
logger.fatal("Error when analyzing edges, aborting new radio connection");
return new RadioConnection(source);
}
/* Create new radio connection using edge hash table */
RadioConnection newConn = new RadioConnection(source);
DestinationRadio[] destinations = getPotentialDestinations(source);
if (destinations == null || destinations.length == 0) {
/* No destinations */
/*logger.info(sendingRadio + ": No dest");*/
return newConn;
}
/*logger.info(source + ": " + destinations.length + " potential destinations");*/
for (DestinationRadio d: destinations) {
DGRMDestinationRadio dest = (DGRMDestinationRadio) d;
if (dest.radio == source) {
/* Fail: cannot receive our own transmission */
/*logger.info(source + ": Fail, receiver is sender");*/
continue;
}
if (dest.ratio < 1.0 && random.nextDouble() > dest.ratio) {
/*logger.info(source + ": Fail, randomly");*/
continue;
}
if (dest.radio.isReceiving()) {
/* Fail: radio is already actively receiving */
/*logger.info(source + ": Fail, receiving");*/
newConn.addInterfered(dest.radio);
/* We will also interfere with the other connection */
dest.radio.interfereAnyReception();
RadioConnection otherConnection = null;
for (RadioConnection conn : getActiveConnections()) {
for (Radio dstRadio : conn.getDestinations()) {
if (dstRadio == dest.radio) {
otherConnection = conn;
break;
}
}
}
if (otherConnection != null) {
otherConnection.addInterfered(dest.radio);
}
continue;
}
if (dest.radio.isInterfered()) {
/* Fail: radio is interfered in another connection */
/*logger.info(source + ": Fail, interfered");*/
newConn.addInterfered(dest.radio);
continue;
}
/* Success: radio starts receiving */
/*logger.info(source + ": OK: " + dest.radio);*/
newConn.addDestination(dest.radio, dest.delay);
}
return newConn;
}
public Collection<Element> getConfigXML() {
ArrayList<Element> config = new ArrayList<Element>();
Element element;
for (Edge edge: edges) {
element = new Element("edge");
element.addContent(edge.getConfigXML());
config.add(element);
}
return config;
}
public boolean setConfigXML(final Collection<Element> configXML, boolean visAvailable) {
random = simulation.getRandomGenerator();
for (Element element : configXML) {
if (element.getName().equals("edge")) {
Edge edge = new Edge();
edge.delayedLoadConfig = element.getChildren();
addEdge(edge);
}
}
requestEdgeAnalysis();
return true;
}
public static class Edge {
public Radio source;
public DestinationRadio superDest;
private Edge() {
/* Internal constructor: await config */
source = null;
superDest = null;
}
public Edge(Radio source, DestinationRadio dest) {
this.source = source;
this.superDest = dest;
}
/* Internal methods */
private Collection<Element> delayedLoadConfig = null; /* Used for restoring edges from config */
private Collection<Element> getConfigXML() {
Vector<Element> config = new Vector<Element>();
Element element;
element = new Element("src");
element.setText(source.getMote().toString());
config.add(element);
element = new Element("dest");
if (superDest.toAll) {
element.setText("ALL");
} else {
element.setText(superDest.radio.getMote().toString());
}
config.add(element);
if (superDest instanceof DGRMDestinationRadio) {
element = new Element("ratio");
element.setText("" + ((DGRMDestinationRadio)superDest).ratio);
config.add(element);
element = new Element("delay");
element.setText("" + ((DGRMDestinationRadio)superDest).delay);
config.add(element);
}
return config;
}
private boolean setConfigXML(Collection<Element> configXML, Simulation simulation) {
Radio dest = null;
double ratio = -1;
long delay = -1;
for (Element element : configXML) {
if (element.getName().equals("src")) {
String moteDescription = element.getText();
boolean foundMote = false;
for (Mote m: simulation.getMotes()) {
if (moteDescription.equals(m.toString())) {
foundMote = true;
source = m.getInterfaces().getRadio();
break;
}
}
if (!foundMote) {
return false;
}
}
if (element.getName().equals("dest")) {
String moteDescription = element.getText();
if (moteDescription.equals("ALL")) {
dest = null; /* ALL */
} else {
boolean foundMote = false;
for (Mote m: simulation.getMotes()) {
if (moteDescription.equals(m.toString())) {
foundMote = true;
dest = m.getInterfaces().getRadio();
break;
}
}
if (!foundMote) {
return false;
}
}
}
if (element.getName().equals("ratio")) {
ratio = Double.parseDouble(element.getText());
}
if (element.getName().equals("delay")) {
delay = Long.parseLong(element.getText());
}
}
if (ratio < 0 || delay < 0) {
return false;
}
superDest = new DGRMDestinationRadio(dest, ratio, delay);
return true;
}
}
}