From 7fd71eabcbc076f6fa78531af8d9c19434bbc39d Mon Sep 17 00:00:00 2001 From: Fredrik Osterlind Date: Thu, 24 Feb 2011 10:23:48 +0100 Subject: [PATCH] towards importing testbed profiles into dgrm --- .../sics/cooja/plugins/DGRMConfigurator.java | 392 +++++--- .../plugins/skins/DGRMVisualizerSkin.java | 9 +- .../radiomediums/DGRMDestinationRadio.java | 95 ++ .../cooja/radiomediums/DestinationRadio.java | 78 ++ .../radiomediums/DirectedGraphMedium.java | 872 ++++++++---------- .../java/se/sics/cooja/radiomediums/UDGM.java | 5 +- 6 files changed, 803 insertions(+), 648 deletions(-) create mode 100644 tools/cooja/java/se/sics/cooja/radiomediums/DGRMDestinationRadio.java create mode 100644 tools/cooja/java/se/sics/cooja/radiomediums/DestinationRadio.java diff --git a/tools/cooja/java/se/sics/cooja/plugins/DGRMConfigurator.java b/tools/cooja/java/se/sics/cooja/plugins/DGRMConfigurator.java index ae363ae97..b1c47b58d 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/DGRMConfigurator.java +++ b/tools/cooja/java/se/sics/cooja/plugins/DGRMConfigurator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Swedish Institute of Computer Science. + * Copyright (c) 2010, Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,9 +32,13 @@ package se.sics.cooja.plugins; import java.awt.BorderLayout; +import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.MouseEvent; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.Observable; import java.util.Observer; @@ -42,12 +46,14 @@ import javax.swing.DefaultCellEditor; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JDialog; +import javax.swing.JFileChooser; import javax.swing.JOptionPane; +import javax.swing.JPanel; import javax.swing.JScrollPane; -import javax.swing.JSpinner; import javax.swing.JTable; import javax.swing.ListSelectionModel; -import javax.swing.SpinnerNumberModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellEditor; @@ -60,8 +66,12 @@ import se.sics.cooja.Mote; import se.sics.cooja.PluginType; import se.sics.cooja.Simulation; import se.sics.cooja.VisPlugin; +import se.sics.cooja.interfaces.Radio; +import se.sics.cooja.radiomediums.AbstractRadioMedium; +import se.sics.cooja.radiomediums.DGRMDestinationRadio; import se.sics.cooja.radiomediums.DirectedGraphMedium; -import se.sics.cooja.radiomediums.DirectedGraphMedium.DGRMDestinationRadio; +import se.sics.cooja.radiomediums.DirectedGraphMedium.Edge; +import se.sics.cooja.util.StringUtils; /** * Simple user interface for configuring edges for the Directed Graph @@ -70,18 +80,19 @@ import se.sics.cooja.radiomediums.DirectedGraphMedium.DGRMDestinationRadio; * @see DirectedGraphMedium * @author Fredrik Osterlind */ -@ClassDescription("DGRM Configurator") +@ClassDescription("DGRM Links") @PluginType(PluginType.SIM_PLUGIN) public class DGRMConfigurator extends VisPlugin { - private static Logger logger = Logger.getLogger(DGRMConfigurator.class); + private static final long serialVersionUID = 4769638341635882051L; + private static Logger logger = Logger.getLogger(DGRMConfigurator.class); private final static int IDX_SRC = 0; private final static int IDX_DST = 1; private final static int IDX_RATIO = 2; - private final static int IDX_DELAY = 3; - private final static int IDX_DEL = 4; - private final static String[] columns = new String[] { - "Source", "Destination", "Success Ratio (%)", "Delay (ms)", "Delete" + private final static int IDX_SIGNAL = 3; + private final static int IDX_DELAY = 4; + private final static String[] COLUMN_NAMES = new String[] { + "Source", "Destination", "RX Ratio", "RSSI", "Delay" }; private GUI gui = null; @@ -89,12 +100,11 @@ public class DGRMConfigurator extends VisPlugin { private Observer radioMediumObserver; private JTable graphTable = null; private JComboBox combo = new JComboBox(); + private JButton removeButton; public DGRMConfigurator(Simulation sim, GUI gui) { super("DGRM Configurator", gui); - this.gui = gui; - radioMedium = (DirectedGraphMedium) sim.getRadioMedium(); /* Listen for graph updates */ @@ -106,55 +116,53 @@ public class DGRMConfigurator extends VisPlugin { /* Represent directed graph by table */ graphTable = new JTable(model) { - public TableCellEditor getCellEditor(int row, int column) { + private static final long serialVersionUID = -4680013510092815210L; + public TableCellEditor getCellEditor(int row, int column) { + combo.removeAllItems(); if (column == IDX_RATIO) { - combo.removeAllItems(); - combo.addItem(1.0); - combo.addItem(0.9); - combo.addItem(0.8); - combo.addItem(0.7); - combo.addItem(0.6); - combo.addItem(0.5); - combo.addItem(0.4); - combo.addItem(0.3); - combo.addItem(0.2); - combo.addItem(0.1); - combo.addItem(0.0); + for (double d=1.0; d >= 0.0; d -= 0.1) { + combo.addItem(d); + } + } else if (column == IDX_SIGNAL) { + for (double d=AbstractRadioMedium.SS_STRONG; d >= AbstractRadioMedium.SS_WEAK; d -= 1) { + combo.addItem((int) d); + } + } else if (column == IDX_DELAY) { + for (double d=0; d <= 5; d++) { + combo.addItem(d); + } } - if (column == IDX_DELAY) { - combo.removeAllItems(); - combo.addItem(0); - combo.addItem(1); - combo.addItem(2); - combo.addItem(3); - combo.addItem(4); - combo.addItem(5); - } - return super.getCellEditor(row, column); } - public String getToolTipText(MouseEvent e) { - java.awt.Point p = e.getPoint(); - int row = rowAtPoint(p); - int col = convertColumnIndexToModel(columnAtPoint(p)); - - /* TODO */ - return super.getToolTipText(); - } }; + graphTable.setFillsViewportHeight(true); combo.setEditable(true); + graphTable.getColumnModel().getColumn(IDX_RATIO).setCellRenderer(new DefaultTableCellRenderer() { - public void setValue(Object value) { + private static final long serialVersionUID = 4470088575039698508L; + public void setValue(Object value) { if (!(value instanceof Double)) { setText(value.toString()); return; } double v = ((Double) value).doubleValue(); - setText((Math.round(v*1000.0) / 10.0) + "%"); + setText(String.format("%1.1f%%", 100*v)); + } + }); + graphTable.getColumnModel().getColumn(IDX_SIGNAL).setCellRenderer(new DefaultTableCellRenderer() { + private static final long serialVersionUID = -7170745293267593460L; + public void setValue(Object value) { + if (!(value instanceof Long)) { + setText(value.toString()); + return; + } + double v = ((Double) value).doubleValue(); + setText(String.format("%1.1f dBm", v)); } }); graphTable.getColumnModel().getColumn(IDX_DELAY).setCellRenderer(new DefaultTableCellRenderer() { - public void setValue(Object value) { + private static final long serialVersionUID = -4669897764928372246L; + public void setValue(Object value) { if (!(value instanceof Long)) { setText(value.toString()); return; @@ -164,21 +172,50 @@ public class DGRMConfigurator extends VisPlugin { } }); graphTable.getColumnModel().getColumn(IDX_RATIO).setCellEditor(new DefaultCellEditor(combo)); + graphTable.getColumnModel().getColumn(IDX_SIGNAL).setCellEditor(new DefaultCellEditor(combo)); graphTable.getColumnModel().getColumn(IDX_DELAY).setCellEditor(new DefaultCellEditor(combo)); graphTable.setAutoResizeMode(JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS); graphTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - + + JPanel southPanel = new JPanel(new GridLayout(1, 3)); JButton button = new JButton("Add"); button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doAddLink(); } }); - final JScrollPane scrollPane = new JScrollPane(graphTable); + southPanel.add(button); + button = new JButton("Remove"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + doRemoveSelectedLink(); + } + }); + removeButton = button; + removeButton.setEnabled(false); + southPanel.add(button); + button = new JButton("Import"); + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + doImportFromFile(); + } + }); + southPanel.add(button); + getContentPane().setLayout(new BorderLayout()); - add(BorderLayout.CENTER, scrollPane); - add(BorderLayout.SOUTH, button); + add(BorderLayout.CENTER, new JScrollPane(graphTable)); + add(BorderLayout.SOUTH, southPanel); + + graphTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + public void valueChanged(ListSelectionEvent e) { + ListSelectionModel lsm = (ListSelectionModel)e.getSource(); + if (e.getValueIsAdjusting()) { + return; + } + removeButton.setEnabled(!lsm.isSelectionEmpty()); + } + }); model.fireTableDataChanged(); setSize(400, 300); @@ -186,29 +223,18 @@ public class DGRMConfigurator extends VisPlugin { private void doAddLink() { JComboBox source = new JComboBox(); - for (int i=0; i < gui.getSimulation().getMotesCount(); i++) { - source.addItem(gui.getSimulation().getMote(i)); - } - JComboBox dest = new JComboBox(); - for (int i=0; i < gui.getSimulation().getMotesCount(); i++) { - dest.addItem(gui.getSimulation().getMote(i)); + for (Mote m: gui.getSimulation().getMotes()) { + source.addItem(m); + dest.addItem(m); } - dest.addItem("ALL"); - - JSpinner ratio = new JSpinner(new SpinnerNumberModel(1.0, 0.0, 1.0, 0.01)); - JSpinner delay = new JSpinner(new SpinnerNumberModel(0, 0, 100, 1)); /* User input */ Object description[] = { - columns[0], + COLUMN_NAMES[0], source, - columns[1], - dest, - columns[2], - ratio, - columns[3], - delay + COLUMN_NAMES[1], + dest }; JOptionPane optionPane = new JOptionPane(); optionPane.setMessage(description); @@ -216,34 +242,20 @@ public class DGRMConfigurator extends VisPlugin { String options[] = new String[] {"Cancel", "Add"}; optionPane.setOptions(options); optionPane.setInitialValue(options[1]); - JDialog dialog = optionPane.createDialog(gui.getTopParentContainer(), title); - dialog.setTitle("Add new link"); + JDialog dialog = optionPane.createDialog(this, title); + dialog.setTitle("Add graph edge"); dialog.setVisible(true); if (optionPane.getValue() == null || !optionPane.getValue().equals("Add")) { return; } /* Register new edge with radio medium */ - DirectedGraphMedium.Edge newEdge; - if (dest.getSelectedItem() instanceof Mote) { - newEdge = new DirectedGraphMedium.Edge( - ((Mote) source.getSelectedItem()).getInterfaces().getRadio(), - new DGRMDestinationRadio( - ((Mote) dest.getSelectedItem()).getInterfaces().getRadio(), - ((Number)ratio.getValue()).doubleValue(), - ((Number)delay.getValue()).longValue() - ) - ); - } else { - newEdge = new DirectedGraphMedium.Edge( - ((Mote) source.getSelectedItem()).getInterfaces().getRadio(), - new DGRMDestinationRadio( - null, - ((Number)ratio.getValue()).doubleValue(), - ((Number)delay.getValue()).longValue() - ) - ); - } + DirectedGraphMedium.Edge newEdge = new DirectedGraphMedium.Edge( + ((Mote) source.getSelectedItem()).getInterfaces().getRadio(), + new DGRMDestinationRadio( + ((Mote) dest.getSelectedItem()).getInterfaces().getRadio() + ) + ); radioMedium.addEdge(newEdge); model.fireTableDataChanged(); } @@ -252,98 +264,180 @@ public class DGRMConfigurator extends VisPlugin { radioMedium.removeEdge(edge); model.fireTableDataChanged(); } + private void doRemoveSelectedLink() { + int firstIndex = graphTable.getSelectedRow(); + if (firstIndex < 0) { + return; + } - final AbstractTableModel model = new AbstractTableModel() { - public String getColumnName(int column) { - if (column < 0 || column >= columns.length) { - logger.fatal("Unknown column: " + column); - return ""; + doRemoveLink(radioMedium.getEdges()[firstIndex]); + } + private void doImportFromFile() { + /* Delete existing edges */ + if (radioMedium.getEdges().length > 0) { + String[] options = new String[] { "Remove", "Cancel" }; + int n = JOptionPane.showOptionDialog( + GUI.getTopParentContainer(), + "Importing edges will remove all your existing edges.", + "Clear edge table?", JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE, null, options, options[0]); + if (n != JOptionPane.YES_OPTION) { + return; + } + for (DirectedGraphMedium.Edge e: radioMedium.getEdges()) { + radioMedium.removeEdge(e); } - return columns[column]; } + /* Select file to import edges from */ + JFileChooser fc = new JFileChooser(); + File suggest = new File(GUI.getExternalToolsSetting("DGRM_IMPORT_LINKS_FILE", "cooja_dgrm_links.dat")); + fc.setSelectedFile(suggest); + int returnVal = fc.showOpenDialog(GUI.getTopParentContainer()); + if (returnVal != JFileChooser.APPROVE_OPTION) { + return; + } + File file = fc.getSelectedFile(); + if (file == null || !file.exists() || !file.canRead()) { + logger.fatal("No read access to file: " + file); + return; + } + GUI.setExternalToolsSetting("DGRM_IMPORT_LINKS_FILE", file.getPath()); + + /* Parse and import edges */ + try { + importEdges(parseDGRMLinksFile(file, gui.getSimulation())); + } catch (Exception e) { + GUI.showErrorDialog(this, "Error when importing DGRM links from " + file.getName(), e, false); + } + } + + private void importEdges(DirectedGraphMedium.Edge[] edges) { + Arrays.sort(edges, new Comparator() { + public int compare(Edge o1, Edge o2) { + return o1.source.getMote().getID() - o2.source.getMote().getID(); + } + }); + for (DirectedGraphMedium.Edge e: edges) { + radioMedium.addEdge(e); + } + logger.info("Imported " + edges.length + " DGRM edges"); + } + + static final int INDEX_SRC = 0; + static final int INDEX_DST = 1; + static final int INDEX_PRR = 2; + static final int INDEX_PRR_CI = 3; + static final int INDEX_NUM_TX = 4; + static final int INDEX_NUM_RX = 5; + static final int INDEX_RSSI_MEDIAN = 6; + static final int INDEX_RSSI_MIN = 7; + static final int INDEX_RSSI_MAX = 8; + public static DirectedGraphMedium.Edge[] parseDGRMLinksFile(File file, Simulation simulation) { + String fileContents = StringUtils.loadFromFile(file); + ArrayList edges = new ArrayList(); + + /* format: # [src] [dst] [prr] [prr_ci] [num_tx] [num_rx] [rssi] [rssi_min] [rssi_max] */ + for (String l: fileContents.split("\n")) { + l = l.trim(); + if (l.startsWith("#")) { + continue; + } + + Mote m; + String[] arr = l.split(" "); + int source = Integer.parseInt(arr[INDEX_SRC]); + m = simulation.getMoteWithID(source); + if (m == null) { + throw new RuntimeException("No simulation mote with ID " + source); + } + Radio sourceRadio = m.getInterfaces().getRadio(); + int dst = Integer.parseInt(arr[INDEX_DST]); + m = simulation.getMoteWithID(dst); + if (m == null) { + throw new RuntimeException("No simulation mote with ID " + dst); + } + DGRMDestinationRadio destRadio = new DGRMDestinationRadio(m.getInterfaces().getRadio()); + double prr = Double.parseDouble(arr[INDEX_PRR]); + /*double prrConfidence = Double.parseDouble(arr[INDEX_PRR_CI]);*/ + /*int numTX <- INDEX_NUM_TX;*/ + /*int numRX <- INDEX_NUM_RX;*/ + double rssi = Double.parseDouble(arr[INDEX_RSSI_MEDIAN]); + /*int rssiMin <- INDEX_RSSI_MIN;*/ + /*int rssiMax <- INDEX_RSSI_MAX;*/ + + DirectedGraphMedium.Edge edge = new DirectedGraphMedium.Edge(sourceRadio, destRadio); + destRadio.delay = 0; + destRadio.ratio = prr; + /*destRadio.prrConfidence = prrConfidence;*/ + destRadio.signal = rssi; + edges.add(edge); + } + return edges.toArray(new DirectedGraphMedium.Edge[0]); + } + + final AbstractTableModel model = new AbstractTableModel() { + private static final long serialVersionUID = 9101118401527171218L; + public String getColumnName(int column) { + if (column < 0 || column >= COLUMN_NAMES.length) { + return ""; + } + return COLUMN_NAMES[column]; + } public int getRowCount() { return radioMedium.getEdges().length; } - public int getColumnCount() { - return columns.length; + return COLUMN_NAMES.length; } - public Object getValueAt(int row, int column) { if (row < 0 || row >= radioMedium.getEdges().length) { - logger.fatal("Unknown row: " + row); return ""; } - if (column < 0 || column >= columns.length) { - logger.fatal("Unknown column: " + column); + if (column < 0 || column >= COLUMN_NAMES.length) { return ""; } - DirectedGraphMedium.Edge edge = radioMedium.getEdges()[row]; if (column == IDX_SRC) { - if (edge.source == null) { - return "?"; - } return edge.source.getMote(); } if (column == IDX_DST) { - if (edge.superDest.toAll) { - return "ALL"; - } return edge.superDest.radio.getMote(); } if (column == IDX_RATIO) { return ((DGRMDestinationRadio)edge.superDest).ratio; } + if (column == IDX_SIGNAL) { + return ((DGRMDestinationRadio)edge.superDest).signal; + } if (column == IDX_DELAY) { return ((DGRMDestinationRadio)edge.superDest).delay / Simulation.MILLISECOND; } - if (column == IDX_DEL) { - return new Boolean(false); - } - - logger.debug("Column data not implemented: " + column); - return "?"; + return ""; } - public void setValueAt(Object value, int row, int column) { if (row < 0 || row >= radioMedium.getEdges().length) { - logger.fatal("Unknown row: " + row); return; } - if (column < 0 || column >= columns.length) { - logger.fatal("Unknown column: " + column); + if (column < 0 || column >= COLUMN_NAMES.length) { return; } DirectedGraphMedium.Edge edge = radioMedium.getEdges()[row]; - if (column == IDX_RATIO) { - /* Success ratio */ - try { - ((DGRMDestinationRadio)edge.superDest).ratio = - ((Number)value).doubleValue(); - radioMedium.requestEdgeAnalysis(); - } catch (ClassCastException e) { - } - return; + try { + if (column == IDX_RATIO) { + ((DGRMDestinationRadio)edge.superDest).ratio = ((Number)value).doubleValue(); + } else if (column == IDX_SIGNAL) { + ((DGRMDestinationRadio)edge.superDest).signal = ((Number)value).doubleValue(); + } else if (column == IDX_DELAY) { + ((DGRMDestinationRadio)edge.superDest).delay = + ((Number)value).longValue() * Simulation.MILLISECOND; + } else { + super.setValueAt(value, row, column); + } + radioMedium.requestEdgeAnalysis(); + } catch (ClassCastException e) { } - if (column == IDX_DELAY) { - /* Propagation delay (ms) */ - try { - ((DGRMDestinationRadio)edge.superDest).delay = - ((Number)value).longValue() * Simulation.MILLISECOND; - radioMedium.requestEdgeAnalysis(); - } catch (ClassCastException e) { - } - return; - } - if (column == IDX_DEL) { - /* Delete link */ - doRemoveLink(edge); - return; - } - super.setValueAt(value, row, column); } public boolean isCellEditable(int row, int column) { @@ -357,24 +451,22 @@ public class DGRMConfigurator extends VisPlugin { return false; } if (column == IDX_DST) { - if (!radioMedium.getEdges()[row].superDest.toAll) { - gui.signalMoteHighlight(radioMedium.getEdges()[row].superDest.radio.getMote()); - } + gui.signalMoteHighlight(radioMedium.getEdges()[row].superDest.radio.getMote()); return false; } if (column == IDX_RATIO) { return true; } - if (column == IDX_DELAY) { + if (column == IDX_SIGNAL) { return true; } - if (column == IDX_DEL) { + if (column == IDX_DELAY) { return true; } return false; } - public Class getColumnClass(int c) { + public Class getColumnClass(int c) { return getValueAt(0, c).getClass(); } }; diff --git a/tools/cooja/java/se/sics/cooja/plugins/skins/DGRMVisualizerSkin.java b/tools/cooja/java/se/sics/cooja/plugins/skins/DGRMVisualizerSkin.java index f0ae3595f..79ab97f76 100644 --- a/tools/cooja/java/se/sics/cooja/plugins/skins/DGRMVisualizerSkin.java +++ b/tools/cooja/java/se/sics/cooja/plugins/skins/DGRMVisualizerSkin.java @@ -48,9 +48,9 @@ import se.sics.cooja.interfaces.Position; import se.sics.cooja.interfaces.Radio; import se.sics.cooja.plugins.Visualizer; import se.sics.cooja.plugins.VisualizerSkin; +import se.sics.cooja.radiomediums.DGRMDestinationRadio; +import se.sics.cooja.radiomediums.DestinationRadio; import se.sics.cooja.radiomediums.DirectedGraphMedium; -import se.sics.cooja.radiomediums.DirectedGraphMedium.DGRMDestinationRadio; -import se.sics.cooja.radiomediums.DirectedGraphMedium.DestinationRadio; @ClassDescription("Radio environment (DGRM)") public class DGRMVisualizerSkin implements VisualizerSkin { @@ -142,16 +142,15 @@ public class DGRMVisualizerSkin implements VisualizerSkin { if (prob == 0.0d) { continue; } - msg = (double)(((int)(1000*prob))/10.0) + "%"; + msg = String.format("%1.1f%%", 100.0*prob); Position pos = r.radio.getPosition(); Point pixel = visualizer.transformPositionToPixel(pos); msgWidth = fm.stringWidth(msg); - g.setColor(Color.LIGHT_GRAY); + g.setColor(new Color(1-(float)prob, (float)prob, 0.0f)); g.drawLine(x, y, pixel.x, pixel.y); g.setColor(Color.BLACK); g.drawString(msg, pixel.x - msgWidth/2, pixel.y + 2*Visualizer.MOTE_RADIUS + 3); } - } public void paintAfterMotes(Graphics g) { diff --git a/tools/cooja/java/se/sics/cooja/radiomediums/DGRMDestinationRadio.java b/tools/cooja/java/se/sics/cooja/radiomediums/DGRMDestinationRadio.java new file mode 100644 index 000000000..cf1dc08ba --- /dev/null +++ b/tools/cooja/java/se/sics/cooja/radiomediums/DGRMDestinationRadio.java @@ -0,0 +1,95 @@ +/* + * 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. + * + * $Id: DirectedGraphMedium.java,v 1.8 2010/12/02 15:25:50 fros4943 Exp $ + */ + +package se.sics.cooja.radiomediums; + +import java.util.Collection; + +import org.jdom.Element; + +import se.sics.cooja.Simulation; +import se.sics.cooja.interfaces.Radio; + +public class DGRMDestinationRadio extends DestinationRadio { + public double ratio = 1.0; /* Link success ratio (per packet). */ + public double signal = AbstractRadioMedium.SS_STRONG; /* RSSI */ + public long delay = 0; /* EXPERIMENTAL: Propagation delay (us). */ + + public DGRMDestinationRadio() { + super(); + } + public DGRMDestinationRadio(Radio dest) { + super(dest); + } + + protected Object clone() { + DGRMDestinationRadio clone = new DGRMDestinationRadio(this.radio); + clone.ratio = this.ratio; + clone.delay = this.delay; + clone.signal = this.signal; + return clone; + } + + public Collection getConfigXML() { + Collection config = super.getConfigXML(); + Element element; + + element = new Element("ratio"); + element.setText("" + ratio); + config.add(element); + + element = new Element("signal"); + element.setText("" + signal); + config.add(element); + + element = new Element("delay"); + element.setText("" + delay); + config.add(element); + + return config; + } + + public boolean setConfigXML(final Collection configXML, Simulation simulation) { + if (!super.setConfigXML(configXML, simulation)) { + return false; + } + for (Element element : configXML) { + if (element.getName().equals("ratio")) { + ratio = Double.parseDouble(element.getText()); + } else if (element.getName().equals("signal")) { + signal = Double.parseDouble(element.getText()); + } else if (element.getName().equals("delay")) { + delay = Long.parseLong(element.getText()); + } + } + return true; + } +} diff --git a/tools/cooja/java/se/sics/cooja/radiomediums/DestinationRadio.java b/tools/cooja/java/se/sics/cooja/radiomediums/DestinationRadio.java new file mode 100644 index 000000000..2613af0c6 --- /dev/null +++ b/tools/cooja/java/se/sics/cooja/radiomediums/DestinationRadio.java @@ -0,0 +1,78 @@ +/* + * 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. + * + * $Id: DirectedGraphMedium.java,v 1.8 2010/12/02 15:25:50 fros4943 Exp $ + */ + +package se.sics.cooja.radiomediums; + +import java.util.ArrayList; +import java.util.Collection; + +import org.apache.log4j.Logger; +import org.jdom.Element; + +import se.sics.cooja.Simulation; +import se.sics.cooja.interfaces.Radio; + +public class DestinationRadio { + private static Logger logger = Logger.getLogger(DestinationRadio.class); + + public Radio radio; /* destination radio */ + public DestinationRadio() { + } + public DestinationRadio(Radio dest) { + this.radio = dest; + } + + public String toString() { + return radio.getMote().toString(); + } + + public Collection getConfigXML() { + ArrayList config = new ArrayList(); + Element element; + + element = new Element("radio"); + element.setText("" + radio.getMote().getID()); + config.add(element); + return config; + } + + public boolean setConfigXML(Collection configXML, Simulation simulation) { + for (Element element : configXML) { + if (element.getName().equals("radio")) { + radio = simulation.getMoteWithID(Integer.parseInt(element.getText())).getInterfaces().getRadio(); + if (radio == null) { + throw new RuntimeException("No mote with ID " + element.getText()); + } + } + } + return true; + } +} diff --git a/tools/cooja/java/se/sics/cooja/radiomediums/DirectedGraphMedium.java b/tools/cooja/java/se/sics/cooja/radiomediums/DirectedGraphMedium.java index adbbaac4b..b61004a48 100644 --- a/tools/cooja/java/se/sics/cooja/radiomediums/DirectedGraphMedium.java +++ b/tools/cooja/java/se/sics/cooja/radiomediums/DirectedGraphMedium.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Swedish Institute of Computer Science. + * Copyright (c) 2010, Swedish Institute of Computer Science. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -36,13 +36,11 @@ 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; @@ -57,496 +55,390 @@ import se.sics.cooja.plugins.skins.DGRMVisualizerSkin; * 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. + * and per-link transmission success ratio/RSSI. * - * @see UDGM + * @see AbstractRadioMedium * @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 edges = new ArrayList(); - private boolean edgesDirty = true; - - /* Used for optimizing lookup time */ - private Hashtable edgesTable = new Hashtable(); - - public DirectedGraphMedium() { - /* Do not initialize radio medium: use only for hash table */ - super(null); - Visualizer.registerVisualizerSkin(DGRMVisualizerSkin.class); - } - - public DirectedGraphMedium(Simulation simulation) { - super(simulation); - this.simulation = simulation; - random = simulation.getRandomGenerator(); - - requestEdgeAnalysis(); - - /* Register plugin and visualizer skin */ - simulation.getGUI().registerPlugin(DGRMConfigurator.class); - Visualizer.registerVisualizerSkin(DGRMVisualizerSkin.class); - } - - public void removed() { - super.removed(); - - /* Unregister plugin and visualizer skin */ - simulation.getGUI().unregisterPlugin(DGRMConfigurator.class); - Visualizer.unregisterVisualizerSkin(DGRMVisualizerSkin.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: getEdges()) { - 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 void updateSignalStrengths() { - - /* Reset signal strengths */ - for (Radio radio : getRegisteredRadios()) { - radio.setCurrentSignalStrength(SS_NOTHING); - } - - /* Set signal strengths */ - RadioConnection[] conns = getActiveConnections(); - for (RadioConnection conn : conns) { - if (conn.getSource().getCurrentSignalStrength() < SS_STRONG) { - conn.getSource().setCurrentSignalStrength(SS_STRONG); - } - for (Radio dstRadio : conn.getDestinations()) { - if (dstRadio.getCurrentSignalStrength() < SS_STRONG) { - dstRadio.setCurrentSignalStrength(SS_STRONG); - } - } - } - - /* Set signal strength to weak on interfered */ - for (RadioConnection conn : conns) { - for (Radio intfRadio : conn.getInterfered()) { - if (intfRadio.getCurrentSignalStrength() < SS_STRONG) { - intfRadio.setCurrentSignalStrength(SS_STRONG); - } - - if (!intfRadio.isInterfered()) { - /*logger.warn("Radio was not interfered");*/ - intfRadio.interfereAnyReception(); - } - } - } - } - - 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> listTable = - new Hashtable>(); - - /* Fill edge hash table with all edges */ - for (Edge edge: getEdges()) { - if (edge.source == null) { - /* XXX Wait until edge configuration has been loaded */ - logger.warn("DGRM edges not loaded"); - return; - } - - ArrayList destRadios; - if (!listTable.containsKey(edge.source)) { - /* Create new source */ - destRadios = new ArrayList(); - } 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 arrTable = - new Hashtable(); - Enumeration 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; - } - - /* Fail if radios are on different (but configured) channels */ - if (source.getChannel() >= 0 && - dest.radio.getChannel() >= 0 && - source.getChannel() != dest.radio.getChannel()) { - continue; - } - - if (!dest.radio.isReceiverOn()) { - /* Fail: radio is off */ - /*logger.info(source + ": Fail, off");*/ - newConn.addInterfered(dest.radio); - continue; - } - - if (dest.ratio < 1.0 && random.nextDouble() > dest.ratio) { - /*logger.info(source + ": Fail, randomly");*/ - /* TODO Interfere now? */ - newConn.addInterfered(dest.radio); - - 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.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 getConfigXML() { - ArrayList config = new ArrayList(); - Element element; - - for (Edge edge: getEdges()) { - element = new Element("edge"); - element.addContent(edge.getConfigXML()); - config.add(element); - } - - return config; - } - - public boolean setConfigXML(final Collection 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 delayedLoadConfig = null; /* Used for restoring edges from config */ - private Collection getConfigXML() { - Vector config = new Vector(); - 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 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; - } - } - + private static Logger logger = Logger.getLogger(DirectedGraphMedium.class); + + private Simulation simulation; + private Random random; + + private ArrayList edges = new ArrayList(); + private boolean edgesDirty = true; + + /* Used for optimizing lookup time */ + private Hashtable edgesTable = new Hashtable(); + + public DirectedGraphMedium() { + /* Do not initialize radio medium: use only for hash table */ + super(null); + Visualizer.registerVisualizerSkin(DGRMVisualizerSkin.class); + } + + public DirectedGraphMedium(Simulation simulation) { + super(simulation); + this.simulation = simulation; + random = simulation.getRandomGenerator(); + + requestEdgeAnalysis(); + + /* Register plugin and visualizer skin */ + simulation.getGUI().registerPlugin(DGRMConfigurator.class); + Visualizer.registerVisualizerSkin(DGRMVisualizerSkin.class); + } + + public void removed() { + super.removed(); + + /* Unregister plugin and visualizer skin */ + simulation.getGUI().unregisterPlugin(DGRMConfigurator.class); + Visualizer.unregisterVisualizerSkin(DGRMVisualizerSkin.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 unregisterRadioInterface(Radio radio, Simulation sim) { + super.unregisterRadioInterface(radio, sim); + + for (Edge edge: getEdges()) { + if (edge.source == radio || edge.superDest.radio == radio) { + removeEdge(edge); + requestEdgeAnalysis(); + } + } + } + + public void updateSignalStrengths() { + + /* Reset signal strengths */ + for (Radio radio : getRegisteredRadios()) { + radio.setCurrentSignalStrength(SS_NOTHING); + } + + /* Set signal strengths */ + RadioConnection[] conns = getActiveConnections(); + for (RadioConnection conn : conns) { + if (conn.getSource().getCurrentSignalStrength() < SS_STRONG) { + conn.getSource().setCurrentSignalStrength(SS_STRONG); + } + for (Radio dstRadio : conn.getDestinations()) { + if (dstRadio.getCurrentSignalStrength() < SS_STRONG) { + dstRadio.setCurrentSignalStrength(SS_STRONG); + } + } + } + + /* Set signal strength to weak on interfered */ + for (RadioConnection conn : conns) { + for (Radio intfRadio : conn.getInterfered()) { + if (intfRadio.getCurrentSignalStrength() < SS_STRONG) { + intfRadio.setCurrentSignalStrength(SS_STRONG); + } + + if (!intfRadio.isInterfered()) { + /*logger.warn("Radio was not interfered");*/ + intfRadio.interfereAnyReception(); + } + } + } + } + + + + /** + * Generates hash table using current edges for efficient lookup. + */ + protected void analyzeEdges() { + Hashtable> listTable = + new Hashtable>(); + + /* Fill edge hash table with all edges */ + for (Edge edge: getEdges()) { + ArrayList destRadios; + if (!listTable.containsKey(edge.source)) { + destRadios = new ArrayList(); + } else { + destRadios = listTable.get(edge.source); + } + + destRadios.add(edge.superDest); + listTable.put(edge.source, destRadios); + } + + /* Convert to arrays */ + Hashtable arrTable = new Hashtable(); + Enumeration 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; + } + + /* Fail if radios are on different (but configured) channels */ + if (source.getChannel() >= 0 && + dest.radio.getChannel() >= 0 && + source.getChannel() != dest.radio.getChannel()) { + continue; + } + + if (!dest.radio.isReceiverOn()) { + /* Fail: radio is off */ + /*logger.info(source + ": Fail, off");*/ + newConn.addInterfered(dest.radio); + continue; + } + + if (dest.ratio < 1.0 && random.nextDouble() > dest.ratio) { + /*logger.info(source + ": Fail, randomly");*/ + /* TODO Interfere now? */ + newConn.addInterfered(dest.radio); + + 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.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 getConfigXML() { + ArrayList config = new ArrayList(); + Element element; + + for (Edge edge: getEdges()) { + element = new Element("edge"); + element.addContent(edge.getConfigXML()); + config.add(element); + } + + return config; + } + + private Collection delayedConfiguration = null; + public boolean setConfigXML(Collection configXML, boolean visAvailable) { + random = simulation.getRandomGenerator(); + + /* Wait until simulation has been loaded */ + delayedConfiguration = configXML; + return true; + } + public void simulationFinishedLoading() { + if (delayedConfiguration == null) { + return; + } + + boolean warnedOldConfig = false; + for (Element element : delayedConfiguration) { + if (element.getName().equals("edge")) { + Collection edgeConfig = element.getChildren(); + Radio source = null; + DestinationRadio dest = null; + for (Element edgeElement : edgeConfig) { + if (edgeElement.getName().equals("src")) { + /* Old version, ignore edge */ + if (!warnedOldConfig) { + logger.fatal("Old simulation config detected: DGRM links will not be imported"); + warnedOldConfig = true; + } + return; + } else if (edgeElement.getName().equals("source")) { + source = simulation.getMoteWithID( + Integer.parseInt(edgeElement.getText())).getInterfaces().getRadio(); + } else if (edgeElement.getName().equals("dest")) { + String destClassName = edgeElement.getText().trim(); + if (destClassName == null || destClassName.isEmpty()) { + continue; + } + Class destClass = + simulation.getGUI().tryLoadClass(this, DestinationRadio.class, destClassName); + if (destClass == null) { + throw new RuntimeException("Could not load class: " + destClassName); + } + try { + dest = destClass.newInstance(); + dest.setConfigXML(edgeElement.getChildren(), simulation); + } catch (Exception e) { + throw (RuntimeException) + new RuntimeException("Unknown class: " + destClassName).initCause(e); + } + } + } + if (source == null || dest == null) { + if (!warnedOldConfig) { + logger.fatal("Old simulation config detected: DGRM links will not be imported"); + warnedOldConfig = true; + } + } else { + addEdge(new Edge(source, dest)); + } + } + } + requestEdgeAnalysis(); + delayedConfiguration = null; + } + + public static class Edge { + public Radio source = null; + public DestinationRadio superDest = null; + + public Edge(Radio source, DestinationRadio dest) { + this.source = source; + this.superDest = dest; + } + + private Collection getConfigXML() { + ArrayList config = new ArrayList(); + Element element; + + element = new Element("source"); + element.setText("" + source.getMote().getID()); + config.add(element); + + element = new Element("dest"); + element.setText(superDest.getClass().getName()); + Collection destConfig = superDest.getConfigXML(); + if (destConfig != null) { + element.addContent(destConfig); + config.add(element); + } + + return config; + } + } } diff --git a/tools/cooja/java/se/sics/cooja/radiomediums/UDGM.java b/tools/cooja/java/se/sics/cooja/radiomediums/UDGM.java index a9b2d6ab3..24ecdc6fd 100644 --- a/tools/cooja/java/se/sics/cooja/radiomediums/UDGM.java +++ b/tools/cooja/java/se/sics/cooja/radiomediums/UDGM.java @@ -43,13 +43,12 @@ 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.SimEventCentral.MoteCountListener; +import se.sics.cooja.Simulation; import se.sics.cooja.interfaces.Position; import se.sics.cooja.interfaces.Radio; import se.sics.cooja.plugins.Visualizer; import se.sics.cooja.plugins.skins.UDGMVisualizerSkin; -import se.sics.cooja.radiomediums.DirectedGraphMedium.DestinationRadio; /** * The Unit Disk Graph Radio Medium abstracts radio transmission range as circles. @@ -184,7 +183,6 @@ public class UDGM extends AbstractRadioMedium { Position senderPos = sender.getPosition(); for (DestinationRadio dest: potentialDestinations) { Radio recv = dest.radio; - Position recvPos = recv.getPosition(); /* Fail if radios are on different (but configured) channels */ if (sender.getChannel() >= 0 && @@ -192,6 +190,7 @@ public class UDGM extends AbstractRadioMedium { sender.getChannel() != recv.getChannel()) { continue; } + Position recvPos = recv.getPosition(); /* Fail if radio is turned off */ // if (!recv.isReceiverOn()) {