Skip to content
Snippets Groups Projects
Commit 89824693 authored by Melanie Bruns's avatar Melanie Bruns
Browse files

Merge branch 'master' into 'release-v4-routing'

Merge improvements into v4

See merge request !144
parents cdab6cf2 030da964
No related branches found
No related tags found
1 merge request!144Merge improvements into v4
Pipeline #
Showing
with 1216 additions and 66 deletions
......@@ -22,6 +22,9 @@ Group.bufferSize = 50M
Group.nrofInterfaces = 1
Group.interface1 = wInterface
Group.net.scanInterval = 30
#After which time messages get dropped (in minutes)
#360 minutes = 6 hours
Group.msgTtl = 360
#How often nodes in the group will reorder the messages in their buffer
Group.MessageOrderingInterval=2.0
# Time the node will help at a disaster site (in seconds)
......@@ -191,7 +194,7 @@ MapBasedMovement.mapFile2 = data/paderborn_pedestrians_and_cars.wkt
MapBasedMovement.mapFile3 = data/paderborn_cars_only.wkt
## Reports - all report names have to be valid report classes
Report.nrofReports = 7
Report.nrofReports = 8
Report.reportDir = reports/
# length of the warm up period (simulated seconds)
Report.warmup = 0
......@@ -204,6 +207,8 @@ Report.report6 = DataSyncReport
DataSyncReport.precision=2
Report.report7 = EnergyLevelReport
EnergyLevelReport.granularity = 600
Report.report8 = BufferOccupancyReport
BufferOccupancyReport.occupancyInterval = 300
## Optimization settings -- these affect the speed of the simulation
## see World class for details.
......
......@@ -100,6 +100,13 @@ public class LocalDatabase {
dataIterator.remove();
}
}
// Make sure to set used size to 0 if database is empty. Without this check, an empty database could use up a
// positive amount of size due to rounding errors.
// This behavior is important when (indirectly) using this field to check whether the database is empty.
if (this.data.isEmpty()) {
this.usedSize = 0;
}
}
/**
......
......@@ -42,7 +42,7 @@ public class Message implements Comparable<Message> {
private double timeReceived;
/** The time when this message was created */
private double timeCreated;
/** Initial TTL of the message */
/** Initial TTL of the message in minutes */
private int initTtl;
/**
......
package core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
/**
* Message which should be delivered to a certain group of nodes
*
......@@ -12,6 +17,11 @@ public class MulticastMessage extends Message {
*/
private Group group;
/**
* Recipients that have been reached on this message copy's path.
*/
private HashSet<Integer> reachedRecipients = new HashSet<>();
/**
* Creates a new Message.
*
......@@ -42,6 +52,7 @@ public class MulticastMessage extends Message {
" but host "+ from + " is not " + to);
}
this.group = to;
this.reachedRecipients.add(from.getAddress());
}
/**
......@@ -63,6 +74,21 @@ public class MulticastMessage extends Message {
return group;
}
/**
* Gets the addresses of all group members that haven't been passed on this message copy's path so far.
*
* @return Addresses of all group members that haven't been reached so far.
*/
public Collection<Integer> getRemainingRecipients() {
List<Integer> remainingRecipients = new ArrayList<>();
for (Integer address : this.group.getMembers()) {
if (!this.reachedRecipients.contains(address)) {
remainingRecipients.add(address);
}
}
return remainingRecipients;
}
/**
* Determines whether the provided node is a final recipient of the message.
* @param host Node to check.
......@@ -80,7 +106,33 @@ public class MulticastMessage extends Message {
*/
@Override
public boolean completesDelivery(DTNHost receiver) {
return false;
// Check whether all hosts have already been reached.
int groupSize = this.group.getMembers().length;
if (this.reachedRecipients.size() == groupSize) {
return true;
}
// Then check if only the current receiver is missing.
return this.reachedRecipients.size() == groupSize - 1
&& this.isFinalRecipient(receiver)
&& !this.reachedRecipients.contains(receiver.getAddress());
}
/**
* Adds a new node on the list of nodes this message has passed
*
* @param node The node to add
*/
@Override
public void addNodeOnPath(DTNHost node) {
super.addNodeOnPath(node);
// Only add a reached recipient if we are not in initialization.
// We cannot add reached recipients in initialization because the necessary fields are not yet set. However,
// adding the single host this message was created by is handled in constructor.
if (this.reachedRecipients != null && this.isFinalRecipient(node)) {
this.reachedRecipients.add(node.getAddress());
}
}
/**
......@@ -111,6 +163,7 @@ public class MulticastMessage extends Message {
public void copyFrom(MulticastMessage m){
super.copyFrom(m);
this.group = m.group;
this.reachedRecipients = new HashSet<>(m.reachedRecipients);
}
/**
......
......@@ -7,7 +7,7 @@ package report;
/**
* Records the average buffer occupancy and its variance with format:
* <p>
* [Simulation time] [average buffer occupancy % [0..100] ] [variance]
* [Simulation time] [average buffer occupancy % [0..100] ] [variance] [min] [max]
* </p>
*
* <p>
......@@ -71,19 +71,23 @@ public class BufferOccupancyReport extends Report implements UpdateListener {
private void printLine(List<DTNHost> hosts) {
double bufferOccupancy = 0.0;
double bo2 = 0.0;
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (DTNHost h : hosts) {
double tmp = h.getBufferOccupancy();
tmp = (tmp<=100.0)?(tmp):(100.0);
bufferOccupancy += tmp;
bo2 += (tmp*tmp)/100.0;
min = Math.min(min, tmp);
max = Math.max(max, tmp);
}
double E_X = bufferOccupancy / hosts.size();
double Var_X = bo2 / hosts.size() - (E_X*E_X)/100.0;
String output = format(SimClock.getTime()) + " " + format(E_X) + " " +
format(Var_X);
format(Var_X) + " " + format(min) + " " + format(max);
write(output);
}
......
......@@ -74,6 +74,20 @@ public class DisasterRouter extends ActiveRouter {
*/
private double powerThreshold;
/**
* Number of tuples of messages/hosts which are remembered, such that they are not sent again
*/
private static final int MESSAGE_HISTORY_SIZE = 1000;
/**
* Constant indicating that a message is not sent because it is contained in the history
*/
private static final int DENIED_IN_HISTORY = -110;
/**
* List storing the last x message IDs and host IDs that are not sent again. The size of the list is restricted to {@link #MESSAGE_HISTORY_SIZE}.
*/
private List<Tuple<String, Integer>> messageSentToHostHistory = new ArrayList<>();
/**
* Initializes a new instance of the {@link DisasterRouter} class.
* @param s Settings to use.
......@@ -251,6 +265,16 @@ public class DisasterRouter extends ActiveRouter {
}
}
/**
* Method is called just before a transfer is finalized
* at {@link #update()}.
* Subclasses that are interested of the event may want to override this.
* @param con The connection whose transfer was finalized
*/
protected void transferDone(Connection con) {
addMessageAndHostToHistory(con.getMessage(), con.getOtherNode(getHost()));
}
/**
* Checks whether this router has anything to send out.
*
......@@ -346,6 +370,22 @@ public class DisasterRouter extends ActiveRouter {
return super.removeFromMessages(id);
}
/**
* Adds a message / host pair to the message history. Also deletes messages until the list has reached its
* maximum size
* @param message: Message to be added
* @param host: Host to be added
*/
private void addMessageAndHostToHistory(Message message, DTNHost host) {
Tuple<String, Integer> historyItem = new Tuple<>(message.getId(), host.getAddress());
while (this.messageSentToHostHistory.size() >= MESSAGE_HISTORY_SIZE) {
this.messageSentToHostHistory.remove(this.messageSentToHostHistory.size() - 1);
}
this.messageSentToHostHistory.add(0, historyItem);
}
/**
* Computes a ratio between the encounter value of this router and the one of the provided router.
* A ratio less than 0.5 signifies that the other host is less social than this one, a
......@@ -407,6 +447,16 @@ public class DisasterRouter extends ActiveRouter {
return messages;
}
@Override
protected int startTransfer(Message m, Connection con) {
if (messageSentToHostHistory.contains(new Tuple<String, Integer>(m.getId(),
con.getOtherNode(getHost()).getAddress()))) {
return DENIED_IN_HISTORY;
}
return super.startTransfer(m, con);
}
/**
* Returns the power threshold.
* @return The power threshold.
......@@ -414,4 +464,20 @@ public class DisasterRouter extends ActiveRouter {
public double getPowerThreshold() {
return this.powerThreshold;
}
/**
* Returns the last {@link #MESSAGE_HISTORY_SIZE} messages that were already sent by this host
* @return message history
*/
public List<Tuple<String, Integer>> getMessageSentToHostHistory() {
return new ArrayList<>(messageSentToHostHistory);
}
/**
* Returns the number of messages in the history
* @return size of the history
*/
public static int getMessageHistorySize() {
return MESSAGE_HISTORY_SIZE;
}
}
......@@ -254,7 +254,7 @@ public class DeliveryPredictabilityStorage extends AbstractIntervalRatingMechani
return this.getDeliveryPredictability(message.getTo());
case MULTICAST:
MulticastMessage multicast = (MulticastMessage)message;
return this.getMaximumDeliveryPredictability(multicast.getGroup().getMembers());
return this.getMaximumDeliveryPredictability(multicast.getRemainingRecipients());
default:
throw new IllegalArgumentException(
"No delivery predictability for messages of type " + message.getType() + " defined!");
......@@ -266,7 +266,7 @@ public class DeliveryPredictabilityStorage extends AbstractIntervalRatingMechani
* @param addresses The addresses to check the delivery predictability for.
* @return The maximum delivery predictability.
*/
private double getMaximumDeliveryPredictability(Integer[] addresses) {
private double getMaximumDeliveryPredictability(Collection<Integer> addresses) {
double maxDeliveryPred = 0;
for (int address : addresses) {
maxDeliveryPred = Math.max(maxDeliveryPred, this.getDeliveryPredictability(address));
......
......@@ -31,6 +31,9 @@ public abstract class AbstractReportTest {
*/
@Before
public void setUp() throws IOException {
// Set locale for periods instead of commas in doubles.
java.util.Locale.setDefault(java.util.Locale.US);
this.outputFile = File.createTempFile("reportTest", ".tmp");
String reportName = this.getReportClass().getSimpleName();
......
package test;
import core.BroadcastMessage;
import core.DTNHost;
import core.Settings;
import core.SimClock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import report.BufferOccupancyReport;
import report.Report;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.DoubleSummaryStatistics;
import java.util.List;
/**
* Contains tests for the {@link report.BufferOccupancyReport} class.
* Created by Britta Heymann on 11.08.2017.
*/
public class BufferOccupancyReportTest extends AbstractReportTest {
/* Time spans needed for tests. */
private static final int REPORT_INTERVAL = WARM_UP_TIME / 2;
private static final int SECOND_REPORT_TIME = 2 * REPORT_INTERVAL;
private static final double SMALL_TIME_DIFFERENCE = 0.01;
/* Message sizes needed for tests. */
private static final int SMALL_MESSAGE_SIZE = 10;
private static final int LARGER_MESSAGE_SIZE = 5000;
/* Indices of certain values in a report line. */
private static final int TIME_INDEX = 0;
private static final int AVERAGE_INDEX = 1;
private static final int VARIANCE_INDEX = 2;
private static final int MINIMUM_INDEX = 3;
private static final int MAXIMUM_INDEX = 4;
/* Further constants. */
private static final double EXPONENT_TO_SQUARE = 2;
private static final String UNEXPECTED_REPORT_TIME = "Expected report line at different simulator time point.";
/** The accepted difference when comparing doubles for equality. */
private static final double DOUBLE_COMPARISON_DELTA = 0.0001;
private BufferOccupancyReport report;
private SimClock clock = SimClock.getInstance();
// Hosts with buffers.
private DTNHost hostWithSmallMessage;
private DTNHost hostWithLargerMessage;
private List<DTNHost> allHosts;
public BufferOccupancyReportTest() {
// Empty constructor for "Classes and enums with private members should hava a constructor" (S1258).
// This is dealt with by the setUp method.
}
@Before
@Override
public void setUp() throws IOException {
// Let base do the basic report setup.
super.setUp();
// Add warm up time.
this.settings.setNameSpace(this.getReportClass().getSimpleName());
this.settings.putSetting(Report.WARMUP_S, Integer.toString(WARM_UP_TIME));
this.settings.putSetting(BufferOccupancyReport.BUFFER_REPORT_INTERVAL, Integer.toString(REPORT_INTERVAL));
this.settings.restoreNameSpace();
// Set clock to 0.
this.clock.setTime(0);
// Create report.
this.report = new BufferOccupancyReport();
// Create hosts.
TestUtils utils = new TestUtils(null, new ArrayList<>(), this.settings);
this.hostWithSmallMessage = utils.createHost();
this.hostWithLargerMessage = utils.createHost();
this.allHosts = Arrays.asList(this.hostWithLargerMessage, this.hostWithSmallMessage);
// Make sure hosts have messages in buffer.
this.hostWithSmallMessage.createNewMessage(
new BroadcastMessage(this.hostWithSmallMessage, "M", SMALL_MESSAGE_SIZE));
this.hostWithLargerMessage.createNewMessage(
new BroadcastMessage(this.hostWithLargerMessage, "M", LARGER_MESSAGE_SIZE));
}
/**
* Checks that the report correctly handles the warm up time as set by the {@link Report#WARMUP_S} setting.
*/
@Override
public void testReportCorrectlyHandlesWarmUpTime() throws IOException {
Assert.assertTrue("Report interval should be smaller than warm up time for test that makes sense.",
REPORT_INTERVAL < WARM_UP_TIME);
// Check report works even before warm up time expired.
this.clock.setTime(REPORT_INTERVAL);
this.report.updated(this.allHosts);
this.report.done();
try(BufferedReader reader = this.createBufferedReader()) {
Assert.assertEquals(
UNEXPECTED_REPORT_TIME,
REPORT_INTERVAL,
BufferOccupancyReportTest.getTimeFromLine(reader.readLine()),
DOUBLE_COMPARISON_DELTA);
Assert.assertNull("Expected only one line.", reader.readLine());
}
}
/**
* Checks that the {@link BufferOccupancyReport} correctly prints all statistics.
* @throws IOException
*/
@Test
public void testBufferStatisticsAreCorrect() throws IOException {
// Write report.
this.clock.setTime(REPORT_INTERVAL);
this.report.updated(Arrays.asList(this.hostWithSmallMessage, this.hostWithLargerMessage));
this.report.done();
// Check statistics.
DoubleSummaryStatistics statistics = new DoubleSummaryStatistics();
statistics.accept(this.hostWithLargerMessage.getBufferOccupancy());
statistics.accept(this.hostWithSmallMessage.getBufferOccupancy());
double variance = 0;
variance += Math.pow(
this.hostWithLargerMessage.getBufferOccupancy() - statistics.getAverage(), EXPONENT_TO_SQUARE);
variance += Math.pow(
this.hostWithSmallMessage.getBufferOccupancy() - statistics.getAverage(), EXPONENT_TO_SQUARE);
try(BufferedReader reader = this.createBufferedReader()) {
String line = reader.readLine();
Assert.assertEquals(
UNEXPECTED_REPORT_TIME,
REPORT_INTERVAL, BufferOccupancyReportTest.getTimeFromLine(line),
DOUBLE_COMPARISON_DELTA);
Assert.assertEquals(
"Expected different value as average.",
statistics.getAverage(), BufferOccupancyReportTest.getAverageFromLine(line),
DOUBLE_COMPARISON_DELTA);
Assert.assertEquals(
"Expected different value as variance.",
variance, BufferOccupancyReportTest.getVarianceFromLine(line),
DOUBLE_COMPARISON_DELTA);
Assert.assertEquals(
"Expected different value as minimum.",
statistics.getMin(), BufferOccupancyReportTest.getMinimumFromLine(line),
DOUBLE_COMPARISON_DELTA);
Assert.assertEquals(
"Expected different value as average.",
statistics.getMax(), BufferOccupancyReportTest.getMaximumFromLine(line),
DOUBLE_COMPARISON_DELTA);
}
}
@Test
public void testCorrectReportIntervalGetsUsed() throws IOException {
// Update report at multiple time points.
this.clock.setTime(REPORT_INTERVAL - SMALL_TIME_DIFFERENCE);
this.report.updated(this.allHosts);
this.clock.setTime(REPORT_INTERVAL);
this.report.updated(this.allHosts);
this.clock.setTime(SECOND_REPORT_TIME);
this.report.updated(this.allHosts);
// Finish report.
this.report.done();
// Check it was printed at correct times.
try(BufferedReader reader = this.createBufferedReader()) {
Assert.assertEquals(
UNEXPECTED_REPORT_TIME,
REPORT_INTERVAL,
BufferOccupancyReportTest.getTimeFromLine(reader.readLine()),
DOUBLE_COMPARISON_DELTA);
Assert.assertEquals(
UNEXPECTED_REPORT_TIME,
SECOND_REPORT_TIME,
BufferOccupancyReportTest.getTimeFromLine(reader.readLine()),
DOUBLE_COMPARISON_DELTA);
Assert.assertNull("Expected only two lines.", reader.readLine());
}
}
@Test
public void testDefaultReportIntervalIsUsedIfNonSpecified() throws IOException {
// Use report without any special settings.
Settings.init(null);
super.setUp();
this.report = new BufferOccupancyReport();
// Update report after default time.
this.clock.setTime(BufferOccupancyReport.DEFAULT_BUFFER_REPORT_INTERVAL);
this.report.updated(this.allHosts);
this.report.done();
// Check line was written at correct time.
try(BufferedReader reader = this.createBufferedReader()) {
Assert.assertEquals(
UNEXPECTED_REPORT_TIME,
BufferOccupancyReport.DEFAULT_BUFFER_REPORT_INTERVAL,
BufferOccupancyReportTest.getTimeFromLine(reader.readLine()),
DOUBLE_COMPARISON_DELTA);
Assert.assertNull("Expected only one line.", reader.readLine());
}
}
/**
* Gets the report class to test.
*
* @return The report class to test.
*/
@Override
protected Class getReportClass() {
return BufferOccupancyReport.class;
}
/**
* Gets the time from a line with format "TIME something else".
* @param line The line to get the time from.
* @return The parsed time.
*/
private static double getTimeFromLine(String line) {
return BufferOccupancyReportTest.parseWordFromLineAsDouble(line, TIME_INDEX);
}
/**
* Gets the reported average from a line with format "something AVERAGE something else".
* @param line The line to get the average from.
* @return The parsed average.
*/
private static double getAverageFromLine(String line) {
return BufferOccupancyReportTest.parseWordFromLineAsDouble(line, AVERAGE_INDEX);
}
/**
* Gets the reported variance from a line with format "something something VARIANCE something else".
* @param line The line to get the variance from.
* @return The parsed variance.
*/
private static double getVarianceFromLine(String line) {
return BufferOccupancyReportTest.parseWordFromLineAsDouble(line, VARIANCE_INDEX);
}
/**
* Gets the reported minimum from a line with format "something something something MINIMUM something else".
* @param line The line to get the minimum from.
* @return The parsed minimum.
*/
private static double getMinimumFromLine(String line) {
return BufferOccupancyReportTest.parseWordFromLineAsDouble(line, MINIMUM_INDEX);
}
/**
* Gets the reported maximum from a line with format "something something something something MAXIMUM".
* @param line The line to get the maximum from.
* @return The parsed maximum.
*/
private static double getMaximumFromLine(String line) {
return BufferOccupancyReportTest.parseWordFromLineAsDouble(line, MAXIMUM_INDEX);
}
/**
* Reads the word with the provided index from the provided line and parses it as a double.
* @param line Line to read the value from.
* @param index Index of word to parse.
* @return The parsed double.
*/
private static double parseWordFromLineAsDouble(String line, int index) {
return Double.parseDouble(line.split(" ")[index]);
}
}
......@@ -356,7 +356,7 @@ public class DeliveryPredictabilityStorageTest {
/**
* Tests that {@link DeliveryPredictabilityStorage#getDeliveryPredictability(Message)} returns the same for a
* multicast as the maximum {@link DeliveryPredictabilityStorage#getDeliveryPredictability(DTNHost)} returns for any
* of its recipients.
* of its remaining recipients.
*/
@Test
public void testGetDeliveryPredictabilityForMulticastMessages() {
......@@ -366,6 +366,7 @@ public class DeliveryPredictabilityStorageTest {
Group group = Group.createGroup(1);
group.addHost(recipient);
group.addHost(oftenMetRecipient);
group.addHost(this.attachedHost);
// Make sure we know both hosts in the group, but know one better than the other.
DeliveryPredictabilityStorage recipientStorage = createDeliveryPredictabilityStorage(recipient);
......@@ -379,12 +380,29 @@ public class DeliveryPredictabilityStorageTest {
> this.dpStorage.getDeliveryPredictability(recipient));
// Check delivery predictability of a multicast message.
Message multicast = new MulticastMessage(recipient, group, "M1", 0);
Message multicast = new MulticastMessage(this.attachedHost, group, "M1", 0);
Assert.assertEquals(
"Multicast delivery predictability should equal the maximum recipient delivery predictability.",
"Multicast delivery predictability should equal the maximum remaining recipient delivery pred.",
this.dpStorage.getDeliveryPredictability(oftenMetRecipient),
this.dpStorage.getDeliveryPredictability(multicast),
DOUBLE_COMPARISON_DELTA);
// Add better known recipient to message path.
multicast.addNodeOnPath(oftenMetRecipient);
// Delivery predictability should have decreased.
Assert.assertEquals(
"Multicast delivery predictability should equal the maximum remaining recipient delivery pred.",
this.dpStorage.getDeliveryPredictability(recipient),
this.dpStorage.getDeliveryPredictability(multicast),
DOUBLE_COMPARISON_DELTA);
// Add other recipient to message path.
multicast.addNodeOnPath(recipient);
// Delivery predictability should now be 0.
Assert.assertEquals("Multicast delivery predictability should be zero if message has all recipients on path.",
0, this.dpStorage.getDeliveryPredictability(multicast), DOUBLE_COMPARISON_DELTA);
}
/**
......
......@@ -3,6 +3,7 @@ package test;
import applications.DatabaseApplication;
import core.BroadcastMessage;
import core.Coord;
import core.DTNHost;
import core.DataMessage;
import core.DisasterData;
import core.Group;
......@@ -18,8 +19,10 @@ import routing.PassiveRouter;
import routing.util.EnergyModel;
import util.Tuple;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* Contains tests for the {@link DisasterRouter} class.
......@@ -974,6 +977,145 @@ public class DisasterRouterTest extends AbstractRouterTest {
h2.getMessageCollection().contains(m1));
}
/**
* Tests whether sent messages are added to the history
*/
public void testMessagesAreAddedToHistory() {
// Start sending a message.
h2.connect(h3);
Message m1 = new Message(h2, h3, "M1", 1);
h2.createNewMessage(m1);
this.updateAllNodes();
// Check the transfer started.
this.mc.next();
this.checkTransferStart(h2, h3, m1.getId());
// Finish sending the message.
this.clock.advance(1);
this.updateAllNodes();
assertTrue("Message should have been added to the history!",
historyContainsMessageAndHost(h2, m1, h3));
}
/**
* Tests whether the messages enter and leave the message history in the correct order
*/
public void testHistoryAcceptsOnlyALimitedNumberOfMessages() {
// Start sending a message.
h2.connect(h3);
Message m1 = new Message(h2, h3, "M1", 1);
h2.createNewMessage(m1);
this.clock.advance(1);
this.updateAllNodes();
// Create as many messages as the history can contain
for (int i=0; i<DisasterRouter.getMessageHistorySize() - 1; i++ ) {
h2.createNewMessage(new Message(h2, h3, "M" + (i+2), 1));
this.clock.advance(1);
this.updateAllNodes();
}
this.clock.advance(1);
this.updateAllNodes();
// Check M1 is still contained
assertTrue("Message should still be contained in the history!",
historyContainsMessageAndHost(h2, m1, h3));
// Add one more message
h2.createNewMessage(new Message(h2, h3, "LastMessage", 1));
this.clock.advance(1);
this.updateAllNodes();
this.clock.advance(1);
this.updateAllNodes();
// Check M1 is not contained
assertFalse("Message should not be contained in the history any more!",
historyContainsMessageAndHost(h2, m1, h3));
}
/**
* Tries to send a message twice from one host to another and another message in between.
* The first message should not be sent a second time.
* @param m1: Message to be sent
* @param receiver: particular host that the message is sent to (important for broadcast and group messages)
*/
private void testMessageIsNotSentTwice(Message m1, DTNHost receiver) {
DTNHost sender = m1.getFrom();
List<Tuple<Message, DTNHost>> sentMessages = new ArrayList<>();
DisasterRouter router = (DisasterRouter)sender.getRouter();
this.mc.reset();
// Send message M1
sender.connect(receiver);
sender.createNewMessage(m1);
this.clock.advance(1);
this.updateAllNodes();
router.deleteMessage(m1.getId(), false);
// Send another message
Message m2 = new Message(sender, receiver, "M2", 1);
sender.createNewMessage(m2);
this.clock.advance(1);
this.updateAllNodes();
router.deleteMessage(m2.getId(), false);
// Try to send message M1 again
sender.createNewMessage(m1);
this.clock.advance(1);
this.updateAllNodes();
this.clock.advance(1);
this.updateAllNodes();
router.deleteMessage(m1.getId(), false);
// Check that no message is sent twice
while (this.mc.next()) {
if (this.mc.getLastType().equals(this.mc.TYPE_START)) {
Tuple<Message, DTNHost> messageTransfer = new Tuple<>(this.mc.getLastMsg(), (DTNHost)this.mc.getLastTo());
assertFalse("Message was sent twice!", sentMessages.contains(messageTransfer));
sentMessages.add(messageTransfer);
}
}
disconnect(receiver);
}
/**
* Creates a direct message, ensures that it is not sent twice
*/
public void testDirectMessagesAreNotSentTwice() {
// Test direct messages
Message directMessage = new Message(h2, h3, "directMessage", 1, PRIORITY);
testMessageIsNotSentTwice(directMessage, h3);
}
/**
* Creates a group message, ensures that it is not sent twice
*/
public void testGroupMessagesAreNotSentTwice() {
// Create groups for multicasts.
Group group = Group.createGroup(0);
group.addHost(h1);
group.addHost(h2);
// Test group messages
Message groupMessage = new MulticastMessage(h1, group, "groupMessage", 1, PRIORITY);
testMessageIsNotSentTwice(groupMessage, h2);
}
/**
* Creates a broadcast message, ensures that it is not sent twice
*/
public void testBroadcastMessagesAreNotSentTwice() {
// Test broadcast messages
Message broadcastMessage = new BroadcastMessage(h1, "broadcastMessage", 1, PRIORITY);
testMessageIsNotSentTwice(broadcastMessage, h3);
}
/**
* Creates a message between h1 and h3 known by h1 and h0.
* Due to its replications density, neither h0 nor h1 will sent it if they are using utility choosers.
......@@ -1001,4 +1143,18 @@ public class DisasterRouterTest extends AbstractRouterTest {
m.addNodeOnPath(this.utils.createHost());
}
}
/**
* Returns true if the current message history contains a pair of message and host
* @param hostFrom: Host of which the history is investigated
* @param message: Message that might be contained
* @param hostTo: Host that might me contained
* @return True if the current message history contains a pair of m and h
*/
public boolean historyContainsMessageAndHost(DTNHost hostFrom, Message message, DTNHost hostTo) {
List<Tuple<String, Integer>> history = ((DisasterRouter)hostFrom.getRouter()).getMessageSentToHostHistory();
return history.contains(new Tuple<>(message.getId(), hostTo.getAddress()));
}
}
......@@ -62,6 +62,7 @@ public class MessageRouterTest {
this.sender = this.utils.createHost();
Group g = Group.createGroup(0);
g.addHost(sender);
g.addHost(this.utils.createHost());
this.msg = new Message(sender, recipient, "M", DEFAULT_MESSAGE_SIZE);
this.broadcast = new BroadcastMessage(sender, "B", DEFAULT_MESSAGE_SIZE);
......
......@@ -6,13 +6,14 @@ import core.Group;
import core.Message;
import core.MessageListener;
import core.MulticastMessage;
import core.NetworkInterface;
import core.SimError;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
......@@ -24,22 +25,33 @@ import static org.junit.Assert.assertTrue;
*/
public class MulticastMessageTest {
private final static int GROUP_ADDRESS_1 = 0;
private final static int GROUP_ADDRESS_2 = 1;
private final static int priority = 7;
private static final int GROUP_ADDRESS_1 = 0;
private static final int GROUP_ADDRESS_2 = 1;
private static final int PRIORITY = 7;
private static final int MESSAGE_SIZE = 100;
private static final int TWO_RECIPIENTS = 2;
private TestUtils utils = new TestUtils(new ArrayList<ConnectionListener>(), new ArrayList<MessageListener>(),
new TestSettings());
private MulticastMessage msg, msgPrio;
private DTNHost from = new TestDTNHost(new ArrayList<NetworkInterface>(), null, null);
private MulticastMessage msg;
private MulticastMessage msgPrio;
private Group group1;
private DTNHost from = this.utils.createHost();
public MulticastMessageTest() {
// Empty constructor for "Classes and enums with private members should hava a constructor" (S1258).
// This is dealt with by the setUp method.
}
@Before
public void setUp() throws Exception {
public void setUp() {
Group.clearGroups();
Group group1 = Group.createGroup(GROUP_ADDRESS_1);
group1.addHost(from);
msg = new MulticastMessage(from, group1, "M", 100);
msgPrio = new MulticastMessage(from, group1, "N", 100, priority);
this.group1 = Group.createGroup(GROUP_ADDRESS_1);
this.group1.addHost(from);
msg = new MulticastMessage(from, this.group1, "M", MESSAGE_SIZE);
msgPrio = new MulticastMessage(from, this.group1, "N", MESSAGE_SIZE, PRIORITY);
}
@Test(expected = java.lang.UnsupportedOperationException.class)
......@@ -67,16 +79,134 @@ public class MulticastMessageTest {
assertFalse(this.msg.isFinalRecipient(host));
}
/**
* Checks that {@link MulticastMessage#completesDelivery(DTNHost)} returns true if all group members have been
* reached before.
*/
@Test
public void testCompletesDeliveryReturnsFalse() {
DTNHost host = this.utils.createHost();
assertFalse(this.msg.completesDelivery(host));
public void testCompletesDeliveryReturnsTrueIfAllGroupMembersHaveBeenReached() {
// Use message to group with two members.
DTNHost member2 = this.utils.createHost();
this.group1.addHost(member2);
// Send message to all group members.
this.from.receiveMessage(this.msg, this.utils.createHost());
member2.receiveMessage(this.msg, this.utils.createHost());
assertTrue(
"Expected delivery to be complete for any host.", this.msg.completesDelivery(this.utils.createHost()));
}
/**
* Checks that {@link MulticastMessage#completesDelivery(DTNHost)} returns true for the last group member to be
* reached.
*/
@Test
public void testCompletesDeliveryReturnsTrueForLastUnreachedGroupMember() {
// Use message to group with three members.
DTNHost member2 = this.utils.createHost();
DTNHost member3 = this.utils.createHost();
this.group1.addHost(member2);
this.group1.addHost(member3);
// Send message to all group members but one.
this.from.receiveMessage(this.msg, this.utils.createHost());
member2.receiveMessage(this.msg, this.utils.createHost());
assertTrue("Expected delivery to be complete with third host.", this.msg.completesDelivery(member3));
}
/**
* Checks that {@link MulticastMessage#completesDelivery(DTNHost)} returns false if a group member is reached, but
* there are still members missing.
*/
@Test
public void testCompletesDeliveryReturnsFalseForUnreachedGroupMemberIfMoreAreMissing() {
// Use message to group with three members.
DTNHost member2 = this.utils.createHost();
DTNHost member3 = this.utils.createHost();
this.group1.addHost(member2);
this.group1.addHost(member3);
// Send message to a single group member.
this.from.receiveMessage(this.msg, this.utils.createHost());
assertFalse("Expected delivery to not be complete with second host.", this.msg.completesDelivery(member2));
}
/**
* Checks that {@link MulticastMessage#completesDelivery(DTNHost)} returns false if only one member is missing and
* the argument is a {@link DTNHost} not in the group.
*/
@Test
public void testCompletesDeliveryReturnsFalseForAlmostCompletedDeliveryToNonGroupMember() {
// Use message to group with three members.
DTNHost member2 = this.utils.createHost();
DTNHost member3 = this.utils.createHost();
this.group1.addHost(member2);
this.group1.addHost(member3);
// Send message to all group members but one.
this.from.receiveMessage(this.msg, this.utils.createHost());
member2.receiveMessage(this.msg, this.utils.createHost());
assertFalse(
"Expected delivery to not be complete with arbitrary third host.",
this.msg.completesDelivery(this.utils.createHost()));
}
/**
* Checks that {@link MulticastMessage#getRemainingRecipients()} returns the expected ones after several hops
* being delivered to some recipients.
*/
@Test
public void testRemainingRecipients() {
// Use message to group with three members.
DTNHost member2 = this.utils.createHost();
DTNHost member3 = this.utils.createHost();
this.group1.addHost(member2);
this.group1.addHost(member3);
// At the beginning, the message should be delivered to its creator, but nobody else.
this.from.createNewMessage(this.msg);
MulticastMessage m = (MulticastMessage)findMessageInHostBuffer(this.msg.getId(), this.from);
Collection<Integer> originalRemainingRecipients = m.getRemainingRecipients();
assertEquals("Expected two remaining recipients.", TWO_RECIPIENTS, originalRemainingRecipients.size());
assertTrue(
"Member 2 should be in remaining recipients.",
originalRemainingRecipients.contains(member2.getAddress()));
assertTrue(
"Member 3 should be in remaining recipients.",
originalRemainingRecipients.contains(member3.getAddress()));
// Deliver to a host not in group. The remaining recipients should not change.
DTNHost nonMember = this.utils.createHost();
this.from.sendMessage(this.msg.getId(), nonMember);
nonMember.messageTransferred(this.msg.getId(), this.from);
MulticastMessage m2 = (MulticastMessage)findMessageInHostBuffer(this.msg.getId(), nonMember);
assertArrayEquals("Remaining recipients should not have changed for first hop.",
originalRemainingRecipients.toArray(), m2.getRemainingRecipients().toArray());
assertArrayEquals("Remaining recipients should not have changed for creator.",
originalRemainingRecipients.toArray(), m.getRemainingRecipients().toArray());
// Deliver to a host in group. The remaining recipients for that host should now only include the last host
// in group.
nonMember.sendMessage(this.msg.getId(), member2);
member2.messageTransferred(this.msg.getId(), nonMember);
MulticastMessage m3 = (MulticastMessage)findMessageInHostBuffer(this.msg.getId(), member2);
assertArrayEquals("Remaining recipients should not have changed for creator.",
originalRemainingRecipients.toArray(), m.getRemainingRecipients().toArray());
assertArrayEquals("Remaining recipients should not have changed for first hop.",
originalRemainingRecipients.toArray(), m2.getRemainingRecipients().toArray());
assertEquals("Expected one remaining recipient for group member.", 1, m3.getRemainingRecipients().size());
assertTrue("Expected third member to still be set as recipient.",
m3.getRemainingRecipients().contains(member3.getAddress()));
}
@Test(expected = SimError.class)
public void testSenderNotInDestinationGroupThrowsError() {
Group group2 = Group.createGroup(GROUP_ADDRESS_2);
MulticastMessage wrongMsg = new MulticastMessage(from, group2, "M", 100);
new MulticastMessage(from, group2, "M", MESSAGE_SIZE);
}
@Test
......@@ -100,9 +230,9 @@ public class MulticastMessageTest {
@Test
public void testGetGroupReturnsTheCorrectGroup() {
MulticastMessage msg = new MulticastMessage(from, Group.getGroup(GROUP_ADDRESS_1), "M", 100);
MulticastMessage message = new MulticastMessage(from, Group.getGroup(GROUP_ADDRESS_1), "M", MESSAGE_SIZE);
assertEquals("Destination group should be Group " + GROUP_ADDRESS_1, Group.getGroup(GROUP_ADDRESS_1),
msg.getGroup());
message.getGroup());
}
@Test
......@@ -114,6 +244,21 @@ public class MulticastMessageTest {
@Test
public void testPriority() {
assertEquals(msg.getPriority(), Message.INVALID_PRIORITY);
assertEquals(msgPrio.getPriority(), priority);
assertEquals(msgPrio.getPriority(), PRIORITY);
}
/**
* Looks for a message with a certain ID in a host's buffer.
* @param id ID to look for.
* @param host The host to search the buffer for.
* @return The message.
*/
private static Message findMessageInHostBuffer(String id, DTNHost host) {
for (Message m : host.getMessageCollection()) {
if (m.getId().equals(id)) {
return m;
}
}
throw new IllegalArgumentException("Cannot find message with id " + id + " in buffer of host " + host);
}
}
package test;
import util.Tuple;
import org.junit.Assert;
import org.junit.Test;
public class TupleTest {
/**
* Some string object
*/
private String text = "testString";
/**
* Another object containing the same string
*/
private String sameText = "testString";
/**
* A different object with a different string
*/
private String otherText = "anotherString";
/**
* Some integer object
*/
private Integer number = 3;
/**
* Another object with the same value
*/
private Integer sameNumber = 3;
/**
* A different object with a different value
*/
private Integer otherNumber = 2;
/**
* Tests whether an object is equal to another object that has the same string / integer value
*/
@Test
public void testObjectsEqualForEqualObjects() {
Tuple<String,Integer> tuple1 = new Tuple<String,Integer>(text,number);
Tuple<String,Integer> tuple2 = new Tuple<String,Integer>(sameText,number);
Assert.assertTrue("Expected objects to be equal!", tuple1.equals(tuple2));
}
/**
* Tests whether objects with different values are unequal
*/
@Test
public void testObjectsDoNotEqualForDifferentObjects() {
Tuple<String,Integer> tuple1 = new Tuple<String,Integer>(text,number);
Tuple<String,Integer> tuple2 = new Tuple<String,Integer>(otherText,number);
Tuple<String,Integer> tuple3 = new Tuple<String,Integer>(text,otherNumber);
Assert.assertFalse("Expected objects to be different!", tuple1.equals(tuple2));
Assert.assertFalse("Expected objects to be different!", tuple1.equals(tuple3));
}
/**
* Tests whether an object has the same hash code as another object that has the same string / integer value
*/
@Test
public void testHashCodeEqualsForEqualObjects() {
Tuple<String,Integer> tuple1 = new Tuple<String,Integer>(text,number);
Tuple<String,Integer> tuple2 = new Tuple<String,Integer>(sameText,number);
Assert.assertTrue("Expected hash codes to be equal!",tuple1.hashCode() == tuple2.hashCode());
}
/**
* Tests whether objects with different values have different hash codes
*/
@Test
public void testHashCodeDoesNotEqualForDifferentObjects() {
Tuple<String,Integer> tuple1 = new Tuple<String,Integer>(text,number);
Tuple<String,Integer> tuple2 = new Tuple<String,Integer>(otherText,number);
Tuple<String,Integer> tuple3 = new Tuple<String,Integer>(text,otherNumber);
Assert.assertFalse("Expected hash codes to be different!", tuple1.hashCode() == tuple2.hashCode());
Assert.assertFalse("Expected hash codes to be different!", tuple1.hashCode() == tuple3.hashCode());
}
/**
* Tests whether an object is equal to itself
*/
@Test
public void testObjectEqualsItself() {
Tuple<String,Integer> tuple = new Tuple<String,Integer>(text,number);
Assert.assertTrue("Expected object to equal itself!", tuple.equals(tuple));
}
/**
* Tests whether an object is equal to null
*/
@Test
public void testObjectDoesNotEqualNull() {
Tuple<String,Integer> tuple = new Tuple<String,Integer>(text,number);
Assert.assertFalse("Expected object to not equal null", tuple.equals(null));
}
/**
* Tests whether an object is equal to an object from another class, e.g. to an integer
*/
@Test
public void testObjectDoesNotEqualObjectFromOtherClass() {
Tuple<String,Integer> tuple = new Tuple<String,Integer>(text,number);
Assert.assertFalse("Expected object to not equal null", tuple.equals(new Integer(42)));
}
}
......@@ -44,4 +44,26 @@ public class Tuple<K,V> {
public String toString() {
return key.toString() + ":" + value.toString();
}
@Override
public boolean equals(Object o) {
// self check
if (this == o) {
return true;
}
// null check, type check and cast
if (o == null || getClass() != o.getClass()) {
return false;
}
Tuple<K,V> t2 = (Tuple<K,V>)o;
return this.key.equals(t2.key) && this.value.equals(t2.value);
}
@Override
public int hashCode() {
return key.hashCode() * value.hashCode() % Integer.MAX_VALUE;
}
}
......@@ -44,7 +44,7 @@ my %msgToCurrCount = ();
my %msgToCurrTimeIdx = ();
# Matches a message line.
my $messageLineMatcher = '^(\d+) \D+(\d+) (\d+)$';
my $messageLineMatcher = '^(\d+) (\D+\d+) (\d+)$';
# Matches the last report line, i.e. the total simulation time.
my $simTimeLineMatcher = '^(\d+)$';
......
......@@ -41,7 +41,7 @@ my %msgToMaxInterval = ();
# Matches a message line.
my $messageLineMatcher = '^\D+(\d+) (\d+) (\d+) (\d+([.]\d+)?)$';
my $messageLineMatcher = '^(\D+\d+) (\d+) (\d+) (\d+([.]\d+)?)$';
# Matches the last report line, i.e. the total simulation time.
my $simTimeLineMatcher = '^(\d+([.]\d+)?)$';
......
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import sys
import re
from readFileUtilities import findNextLineContaining
# Script that translates broadcast analysis into plots of average and minimum message distribution over time.
# Takes as arguments
# (1) a broadcast analysis file
# (2) path to save the graphic to.
#
# The broadcast analysis file should have a format like:
#
# Reached people by broadcasts of prio 2 by time after creation:
# time avg min
# 300 22.75 0
# 600 156.19 0
# 900 380.53 0
# ...
#
# Reached people by broadcasts of prio 3 by time after creation:
# time avg min
# 300 22.13 0
# ...
# In a broadcast analysis file given as a collection of lines, finds all lines concerning the specified priority.
def findLinesConcerningPriority(all_lines, priority):
correctPriorityLine = findNextLineContaining(
all_lines, 0, "Reached people by broadcasts of prio {} by time after creation:".format(priority))
nextPriorityLine = findNextLineContaining(all_lines, correctPriorityLine + 1, "prio")
return all_lines[correctPriorityLine:nextPriorityLine]
# Draws two functions over the same x values.
# Labels are selected as appropriate for broadcast analysis.
def drawPlots(x, y_minimum, y_average, priority):
plt.title('Broadcast distribution\n Priority {}'.format(priority))
plt.xlabel('Minutes since message creation')
plt.ylabel('Reached people')
plt.plot(x, y_minimum, '.-', label='Minimum')
plt.plot(x, y_average, '.-', label='Average')
plt.legend(loc='upper left')
plt.grid(True)
axes = plt.gca()
axes.set_xlim(xmin = 0)
axes.set_ylim(ymin = 0)
axes.xaxis.set_major_locator(ticker.IndexLocator(base=60, offset=-5))
def plotAnalysis(lines, priority, figure, plotNumber):
# Interpret lines to find minimum and average number of reached people over time
timePoints = []
minimum = []
average = []
for line in lines:
match = re.match("(\d+)\s+(\d+.\d+)\s+(\d+)", line)
if match is None:
continue
timePoints.append(float(match.group(1)) / 60)
average.append(float(match.group(2)))
minimum.append(int(match.group(3)))
# Draw plots.
figure.add_subplot(1, 3, plotNumber)
drawPlots(timePoints, minimum, average, priority)
# Main function of the script. See script description at the top of the file for further information.
def main(analysisFileName, graphicFileName):
# Read broadcast analysis from file
with open(analysisFileName) as analysis_file:
analysis = analysis_file.readlines()
# Only look at priorities 2, 5 and 9
prio2Analysis = findLinesConcerningPriority(analysis, 2)
prio5Analysis = findLinesConcerningPriority(analysis, 5)
prio9Analysis = findLinesConcerningPriority(analysis, 9)
# Draw plots for all those priorities.
fig = plt.figure(figsize=(16, 4))
plotAnalysis(prio2Analysis, 2, fig, 1)
plotAnalysis(prio5Analysis, 5, fig, 2)
plotAnalysis(prio9Analysis, 9, fig, 3)
# Save to file
plt.tight_layout()
plt.savefig(graphicFileName)
plt.close()
# Make sure script can be called from command line.
if __name__ == "__main__":
main(sys.argv[1], sys.argv[2])
import matplotlib.pyplot as plt
import sys
import re
# Script that generates plots about buffer occupancy from buffer occupancy report
# Takes as arguments
# (1) a buffer occupancy report
# (2) path to save the graphic to.
#
# The buffer occupancy report file should have a format like:
# [Simulation time] [average buffer occupancy % [0..100] ] [variance] [min] [max]
def main(analysisFileName, graphicFileName):
# Read delay analysis from file
with open(analysisFileName) as analysis_file:
analysis = analysis_file.readlines()
times = []
avgOccupancies = []
minima = []
maxima = []
variances = []
for line in analysis:
match = re.match("(\d+\.\d+\s)(\d+\.\d+\s)(\d+\.\d+\s)(\d+\.\d+\s)(\d+\.\d+)", line)
if match is None:
print("no match")
continue
simTime = float(match.group(1)) / 60
avgOccupancy = float(match.group(2))
variance = float(match.group(3))
min = float(match.group(4))
max = float(match.group(5))
times.append(simTime)
avgOccupancies.append(avgOccupancy)
minima.append(min)
maxima.append(max)
variances.append(variance)
plt.grid(True)
plt.plot(times, maxima, label="maximum")
plt.plot(times, avgOccupancies, label="mean")
plt.plot(times, minima, label="minimum")
plt.plot(times, variances, label="variance")
plt.xlabel('Time in minutes')
plt.ylabel('Percentage of buffer that is occupied')
plt.legend(loc='right')
plt.title('Buffer occupancy')
plt.savefig(graphicFileName, dpi=300)
plt.close()
# Make sure script can be called from command line.
if __name__ == "__main__":
main(sys.argv[1], sys.argv[2])
\ No newline at end of file
import matplotlib.pyplot as plt
import sys
import re
import matplotlib.ticker as ticker
# Script that translates a data sync report into plots of
# * memory consumption,
# * average data utility,
# * data distance and
# * data age
# over time and adds a pie chart about data distribution at the end of the simulation.
#
# Takes as arguments
# (1) a data sync report file and
# (2) a path to save the resulting graphic to.
#
# Data sync stats for scenario realisticScenario
# sim_time: 60.00, avg_used_mem: 0.00%, max_used_mem: 0.00%, med_avg_data_util: 0.99, avg_data_util: 0.99, med_avg_data_age: 21.15, avg_data_age: 24.56, med_max_data_age: 21.15, med_avg_data_dist: 97.41, avg_data_dist: 90.05, med_max_data_dist: 97.41
# avg_ratio_map: 0.00%, avg_ratio_marker: 29.41%, avg_ratio_skill: 47.06%, avg_ratio_res: 23.53%
#
# sim_time: 120.10, avg_used_mem: 0.00%, max_used_mem: 0.00%, med_avg_data_util: 0.99, avg_data_util: 0.98, med_avg_data_age: 42.55, avg_data_age: 54.16, med_max_data_age: 42.55, med_avg_data_dist: 117.17, avg_data_dist: 146.08, med_max_data_dist: 117.17
# avg_ratio_map: 0.00%, avg_ratio_marker: 23.64%, avg_ratio_skill: 32.73%, avg_ratio_res: 43.64%
# ...
def main(analysisFileName, graphicFileName):
# Read data sync report from file
with open(analysisFileName) as analysis_file:
report = analysis_file.readlines()
# Interpret lines to find memory consumption, data utility, data distance and data age metrics over time
timePoints = []
averageMemoryConsumption = []
maximumMemoryConsumption = []
medianAverageDataUtility = []
averageAverageDataUtility = []
medianAverageDataAge = []
averageAverageDataAge = []
medianMaximumDataAge = []
medianAverageDataDistance = []
averageAverageDataDistance = []
medianMaximumDataDistance = []
nextTimePoint = 0
for line in report:
match = re.match("sim_time: (\d+.\d+), "
"avg_used_mem: (\d+.\d+)%, ""max_used_mem: (\d+.\d+)%, "
"med_avg_data_util: (\d+.\d+), avg_data_util: (\d+.\d+), "
"med_avg_data_age: (\d+.\d+), avg_data_age: (\d+.\d+), med_max_data_age: (\d+.\d+), "
"med_avg_data_dist: (\d+.\d+), avg_data_dist: (\d+.\d+), med_max_data_dist: (\d+.\d+)", line)
# Skip lines not detailing desired metrics
if match is None:
continue
# Only check every 10 minutes
timePoint = float(match.group(1)) / 60
if (timePoint < nextTimePoint):
continue
nextTimePoint = timePoint + 10
# Make sure to use correct unit (minutes, kilometres)
timePoints.append(timePoint)
averageMemoryConsumption.append(float(match.group(2)))
maximumMemoryConsumption.append(float(match.group(3)))
medianAverageDataUtility.append(float(match.group(4)))
averageAverageDataUtility.append(float(match.group(5)))
medianAverageDataAge.append(float(match.group(6)) / 60)
averageAverageDataAge.append(float(match.group(7)) / 60)
medianMaximumDataAge.append(float(match.group(8)) / 60)
medianAverageDataDistance.append(float(match.group(9)) / 1000)
averageAverageDataDistance.append(float(match.group(10)) / 1000)
medianMaximumDataDistance.append(float(match.group(11)) / 1000)
# Create four graphics over time: one each for memory consumption, data utility, data distance and data age
fig = plt.figure(figsize=(12,12))
# Adds a subplot over time at the specified index.
# Title, label of y axis and plots (in the form of values and a label) can be provided.
def addSubplot(atIndex, title, ylabel, plots):
fig.add_subplot(3, 2, atIndex)
plt.title(title)
plt.xlabel('Minutes in simulation')
plt.ylabel(ylabel)
for (values, label) in plots:
plt.plot(timePoints, values, '.-', label=label)
plt.legend(loc='upper left')
plt.grid(True)
axes = plt.gca()
axes.set_xlim(xmin=0)
axes.set_ylim(ymin=0)
axes.xaxis.set_major_locator(ticker.IndexLocator(base=60, offset=-1))
addSubplot(3, title='Memory Consumption',
ylabel='Used memory',
plots=[(averageMemoryConsumption, 'Average'), (maximumMemoryConsumption, 'Maximum')])
addSubplot(2, title='Average Data Utility in Local Database',
ylabel='Average data utility',
plots=[(medianAverageDataUtility, 'Median'), (averageAverageDataUtility, 'Average')])
addSubplot(4, title='Data Distance',
ylabel='Distance in km',
plots=[(averageAverageDataDistance, 'Average Average'),
(medianAverageDataDistance, 'Median Average'),
(medianMaximumDataDistance, 'Median Maximum')])
addSubplot(6, title='Data Age',
ylabel='Age in minutes',
plots=[(averageAverageDataAge, 'Average Average'),
(medianAverageDataAge, 'Median Average'),
(medianMaximumDataAge, 'Median Maximum')])
axes = plt.gca()
axes.yaxis.set_major_locator(ticker.IndexLocator(base=60, offset=-0.4))
# Add information about data distribution
# Get information
match = re.match("avg_ratio_map: (\d+.\d+)%, avg_ratio_marker: (\d+.\d+)%, avg_ratio_skill: (\d+.\d+)%, avg_ratio_res: (\d+.\d+)%",
report[-2])
labels = ['Marker', 'Skill', 'Resource']
values = [float(match.group(2)), float(match.group(3)), float(match.group(4))]
fig.add_subplot(3, 2, 1)
# Create pie chart
plt.title('Average final distribution in local database')
plt.pie(values)
plt.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle.
plt.legend(loc = 'lower left',
labels=['{p:4.1f}% \t {n}'.format(p=value, n=label).expandtabs() for label, value in zip(labels, values)])
# Save to file
plt.tight_layout()
plt.savefig(graphicFileName, dpi = 300)
plt.close()
# Make sure script can be called from command line.
if __name__ == "__main__":
main(sys.argv[1], sys.argv[2])
\ No newline at end of file
  • Contributor

    SonarQube analysis reported no issues.

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment