towards importing testbed profiles into dgrm
This commit is contained in:
parent
fa0fbf5dab
commit
7fd71eabcb
|
@ -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<DirectedGraphMedium.Edge>() {
|
||||
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<DirectedGraphMedium.Edge> edges = new ArrayList<DirectedGraphMedium.Edge>();
|
||||
|
||||
/* 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<? extends Object> getColumnClass(int c) {
|
||||
return getValueAt(0, c).getClass();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<Element> getConfigXML() {
|
||||
Collection<Element> 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<Element> 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;
|
||||
}
|
||||
}
|
|
@ -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<Element> getConfigXML() {
|
||||
ArrayList<Element> config = new ArrayList<Element>();
|
||||
Element element;
|
||||
|
||||
element = new Element("radio");
|
||||
element.setText("" + radio.getMote().getID());
|
||||
config.add(element);
|
||||
return config;
|
||||
}
|
||||
|
||||
public boolean setConfigXML(Collection<Element> 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;
|
||||
}
|
||||
}
|
|
@ -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<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);
|
||||
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<Radio,ArrayList<DestinationRadio>> listTable =
|
||||
new Hashtable<Radio,ArrayList<DestinationRadio>>();
|
||||
|
||||
/* 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<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;
|
||||
}
|
||||
|
||||
/* 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<Element> getConfigXML() {
|
||||
ArrayList<Element> config = new ArrayList<Element>();
|
||||
Element element;
|
||||
|
||||
for (Edge edge: getEdges()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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<Radio,ArrayList<DestinationRadio>> listTable =
|
||||
new Hashtable<Radio,ArrayList<DestinationRadio>>();
|
||||
|
||||
/* Fill edge hash table with all edges */
|
||||
for (Edge edge: getEdges()) {
|
||||
ArrayList<DestinationRadio> destRadios;
|
||||
if (!listTable.containsKey(edge.source)) {
|
||||
destRadios = new ArrayList<DestinationRadio>();
|
||||
} else {
|
||||
destRadios = listTable.get(edge.source);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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<Element> getConfigXML() {
|
||||
ArrayList<Element> config = new ArrayList<Element>();
|
||||
Element element;
|
||||
|
||||
for (Edge edge: getEdges()) {
|
||||
element = new Element("edge");
|
||||
element.addContent(edge.getConfigXML());
|
||||
config.add(element);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
private Collection<Element> delayedConfiguration = null;
|
||||
public boolean setConfigXML(Collection<Element> 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<Element> 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<? extends DestinationRadio> 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<Element> getConfigXML() {
|
||||
ArrayList<Element> config = new ArrayList<Element>();
|
||||
Element element;
|
||||
|
||||
element = new Element("source");
|
||||
element.setText("" + source.getMote().getID());
|
||||
config.add(element);
|
||||
|
||||
element = new Element("dest");
|
||||
element.setText(superDest.getClass().getName());
|
||||
Collection<Element> destConfig = superDest.getConfigXML();
|
||||
if (destConfig != null) {
|
||||
element.addContent(destConfig);
|
||||
config.add(element);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
|
Loading…
Reference in a new issue