/* * Copyright (c) 2005-2010 KOM – Multimedia Communications Lab * * This file is part of PeerfactSim.KOM. * * PeerfactSim.KOM is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * PeerfactSim.KOM is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with PeerfactSim.KOM. If not, see . * */ package de.tud.kom.p2psim.impl.linklayer.mac; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Random; import de.tud.kom.p2psim.api.analyzer.LinklayerAnalyzer; import de.tud.kom.p2psim.api.analyzer.MessageAnalyzer; import de.tud.kom.p2psim.api.analyzer.MessageAnalyzer.Reason; import de.tud.kom.p2psim.api.common.SimHost; import de.tud.kom.p2psim.api.energy.ComponentType; import de.tud.kom.p2psim.api.energy.EnergyCommunicationComponent; import de.tud.kom.p2psim.api.energy.EnergyEventListener; import de.tud.kom.p2psim.api.linklayer.LinkLayerMessage; import de.tud.kom.p2psim.api.linklayer.LinkMessageEvent; import de.tud.kom.p2psim.api.linklayer.LinkMessageListener; import de.tud.kom.p2psim.api.linklayer.mac.Link; import de.tud.kom.p2psim.api.linklayer.mac.MacAddress; import de.tud.kom.p2psim.api.linklayer.mac.MacEventInformation; import de.tud.kom.p2psim.api.linklayer.mac.MacLayer; import de.tud.kom.p2psim.api.linklayer.mac.PhyType; import de.tud.kom.p2psim.api.network.BandwidthImpl; import de.tud.kom.p2psim.api.scenario.ConfigurationException; import de.tud.kom.p2psim.api.topology.views.TopologyView; import de.tud.kom.p2psim.impl.linklayer.ModularLinkLayer; import de.tud.kom.p2psim.impl.network.BandwidthEstimator; import de.tud.kom.p2psim.impl.util.LiveMonitoring; import de.tud.kom.p2psim.impl.util.LiveMonitoring.ProgressValue; import de.tudarmstadt.maki.simonstrator.api.Event; import de.tudarmstadt.maki.simonstrator.api.Message; import de.tudarmstadt.maki.simonstrator.api.Monitor; import de.tudarmstadt.maki.simonstrator.api.Randoms; import de.tudarmstadt.maki.simonstrator.api.Time; import de.tudarmstadt.maki.simonstrator.api.component.core.MonitorComponent.AnalyzerNotAvailableException; import de.tudarmstadt.maki.simonstrator.api.component.network.NetID; /** * Basic implementation of a MacLayer running on a PHY-CommunicationComponent. * As with the {@link ModularLinkLayer} you should extend this class to add a * more advanced MAC to ensure basic functionality and consistent behavior. For * this reason, some methods in this class are marked as final... * * If a MAC wants to implement a protocol (ie. send messages to other MACs * without triggering a deliver to higher layers) it can do so by implementing a * corresponding Message type and/or {@link MacEventInformation}. A Message * is only delivered to the higher layers if notifyLinkLayer is called. * * This Layer takes care of energy consumption, as long as the sendUnicast and * sendBroadcast-methods are used to dispatch all messages (also the * control-messages, if a protocol is implemented) * * @author Bjoern Richerzhagen * @version 1.0, 21.02.2012 */ public abstract class AbstractMacLayer implements MacLayer { public static enum DropReason { LINK_DROP, QUEUE_FULL, QUEUE_TIMEOUT, NO_LINK, SENDING_MAC_OFFLINE, RECEIVING_MAC_OFFLINE } /** * The PHY we operate on */ private PhyType phy; /** * The {@link TopologyView} we operate on */ private TopologyView topoView; /** * Our own Host */ private SimHost host; /** * Our own MacAddress on this PHY */ private MacAddress macAddress; /** * Our own IP used by the NetLayer on this PHY. Not available during initialization. */ private NetID netID; /** * Each MAC has a Message queue for outgoing messages */ private LinkedList queue; /** * The LinkLayer registers itself as a MessageListener */ private LinkMessageListener messageListener; /** * Number of retransmissions before the message is dropped. * provideErrorControl must be true. */ private final int maxRetransmissions; /** * If this is set to true, the MAC will request broken packets * maxRetransmission times. */ private final boolean enableErrorControl; /** * */ private boolean isOnline; /** * The maximum length of the outgoing queue (if 0, queue has no limit) */ private final int maxQueueLength; /** * The maximum time a message is kept in the queue before it is dropped (if * 0, there is no limit) */ private final long maxTimeInQueue; /** * An {@link EnergyCommunicationComponent} for this MAC */ private EnergyCommunicationComponent energyComponent; /** * Contains the configured bandwidth for this MAC */ protected final BandwidthImpl maximumBandwidth; /** * Returns an approximation of the current bandwidth */ protected final BandwidthEstimator currentBandwidth; private final Random random = Randoms.getRandom(AbstractMacLayer.class); /* * Analyzing */ public static boolean[] _analyzersInitialized = new boolean[PhyType .values().length]; public static MacMessagesDropReasonProgress[] _dropReasonAnalyzer = new MacMessagesDropReasonProgress[PhyType .values().length]; public static final int MESSAGE_DROPPED = 1; public static final int MESSAGE_SENT = 2; public static final int MESSAGE_RECEIVED = 3; public static final int MESSAGE_AT_SUBNET = 4; /** * Create a new MacLayer without bounds on the outgoing message queue (ie. * endless size, no limit on the waiting time). * * @param ownMacAddress * @param phy * @param maxRetransmissions * before the message is dropped. note: Broadcasts are never * retransmitted. Set this to zero to enable ErrorControl at the * MAC * @param bandwidth * the maximum BW of this MAC (i.e. the BW that would be achieved * if messages are not queued) */ public AbstractMacLayer(SimHost host, MacAddress ownMacAddress, PhyType phy, int maxRetransmissions, BandwidthImpl bandwidth) { this(host, ownMacAddress, phy, 0, 0, maxRetransmissions, bandwidth); } /** * Create a new MacLayer with a bounded outgoing queue (max length) and a * timeout for messages in said queue. * * @param ownMacAddress * @param phy * @param maxQueueLength * if 0, the queue is not limited * @param maxTimeInQueue * if 0, there is no timeout for messages in the queue * @param maxRetransmissions * before the message is dropped. note: Broadcasts are never * retransmitted * @param bandwidth * the maximum BW of this MAC (i.e. the BW that would be achieved * if messages are not queued) */ public AbstractMacLayer(SimHost host, MacAddress ownMacAddress, PhyType phy, int maxQueueLength, long maxTimeInQueue, int maxRetransmissions, BandwidthImpl bandwidth) { this.host = host; this.enableErrorControl = maxRetransmissions > 0; this.macAddress = ownMacAddress; this.phy = phy; this.maxQueueLength = maxQueueLength; this.maxTimeInQueue = maxTimeInQueue; this.maxRetransmissions = maxRetransmissions; this.maximumBandwidth = bandwidth; // ensure, that max and current are NOT the same object instance! this.currentBandwidth = new BandwidthEstimator(5); // 5s historical data this.queue = new LinkedList(); if (!_analyzersInitialized[phy.ordinal()]) { _dropReasonAnalyzer[phy.ordinal()] = new MacMessagesDropReasonProgress(); LiveMonitoring.addProgressValueIfNotThere(_dropReasonAnalyzer[phy .ordinal()]); _analyzersInitialized[phy.ordinal()] = true; } } @Override public void initialize() throws ConfigurationException { /* * fetch an energy-component if one is present */ if (host.getEnergyModel() != null) { List energyComponents = host .getEnergyModel().getComponents( ComponentType.COMMUNICATION, EnergyCommunicationComponent.class); for (EnergyCommunicationComponent comp : energyComponents) { if (comp.getPhyType().equals(phy)) { energyComponent = comp; } } } if (energyComponent == null) { // Log.warn("The MAC-Layer did not find an Energy-Component for " // + phy.toString() // + // ". This is fine, if you did not intend to measure energy consumption. Otherwise, please check your configuration!"); energyComponent = new EnergyComponentStub(phy); } /* * Fetch a TopologyView for this PHY */ if (host.getTopologyComponent() == null || host.getTopologyComponent().getTopology() == null) { throw new ConfigurationException( "In order to use the LinkLayer you have to specify a Topology as a seperate component of your hosts."); } topoView = host.getTopologyComponent().getTopology() .getTopologyView(phy); if (topoView == null) { throw new ConfigurationException( "There is no TopologyView for the PHY " + phy.toString() + " configured!"); } } @Override public void shutdown() { throw new AssertionError("You are not supposed to shutdown a MAC."); } @Override public final TopologyView getTopologyView() { return topoView; } /** * Use this component to account for energy consumption. This is safe to * use, as it is always backed by a stub - it is never null. * * @return */ @Override public EnergyCommunicationComponent getEnergyComponent() { return energyComponent; } /** * Current size of the outgoing Queue * * @return */ protected final int getQueueSize() { return queue.size(); } /** * Maximum length of the outgoing queue, if equal to zero there is no limit * * @return */ protected final int getMaxQueueLength() { return maxQueueLength; } /** * Gets the maximal retransmissions. * * @return the maximal retransmissions. */ public final int getMaxRetransmissions() { return maxRetransmissions; } /** * Maximum time a message is kept in the outgoing queue. If equal to zero * there is no limit. * * @return */ protected final long getMaxTimeInQueue() { return maxTimeInQueue; } /** * Retrieves and removes the first element in the Queue, after all * entries that already timed out are deleted. * * @return */ protected final QueueEntry getQueueHead() { removeOutdatedQueueEntries(); return queue.poll(); } /** * Removes all messages that are already waiting longer than maxTimeInQueue, * notifying messageDropped() for each dropped message. The reason will be * QUEUE_TIMEOUT in this case. * */ private void removeOutdatedQueueEntries() { if (maxTimeInQueue == 0) { return; } long currentTime = Time.getCurrentTime(); QueueEntry peek = queue.peek(); while (peek != null && peek.getTimeEntered() + maxTimeInQueue < currentTime) { queue.poll(); messageDropped(DropReason.QUEUE_TIMEOUT, peek.getMessage()); peek = queue.peek(); } } /** * This is called whenever a drop occurs within the MAC/PHY. A great * opportunity to add an Analyzer or some error handling for higher layers. * * Please note, that this might happen asynchronously - the messages are not * removed from the queue the second their timeout expires but instead as * soon as a new send() or a new getQueueHead() is issued. * * @param reason * @param msg */ protected void messageDropped(DropReason reason, Message msg) { ModularLinkLayer._linkDropped++; _dropReasonAnalyzer[phy.ordinal()].increment(reason); // System.out.println(Simulator.getFormattedTime(Simulator // .getCurrentTime()) // + " LinkLayer DROP " // + reason.toString() // + " of " + msg.toString()); if (msg instanceof LinkLayerMessage) { _linkMsgEvent((LinkLayerMessage) msg, getHost(), MessageAnalyzer.Reason.DROP); } } /** * Notification: a new Entry was added to the outgoing queue. */ abstract protected void handleNewQueueEntry(); /** * Notification: a message arrived! * * @param message * the message itself * @param info * additional eventInformation */ abstract protected void handleReceivedMessage(MacEventInformation eventInfo); /** * This is to be called by the MAC if a Message should be passed to upper * layers (ie. it is no MAC_ctrl-Message) * * @param eventInfo */ protected final void notifyLinkLayer(LinkMessageEvent eventInfo) { if (this.isOnline()) { messageListener.messageArrived(eventInfo); } } /** * Send a Message to the MAC on the receiver-side (event scheduling). * * @param receiver * @param eventInformation * @param delay * time it takes the message to reach the receiver * @param dropped * receive-events are also used in the case of a message being * dropped. This will enable a persistent callback on the sending * mac as soon as the message is or would be received. */ protected final void scheduleReceive(MacLayer receiver, MacEventInformation eventInformation, long delay, boolean dropped) { // Event assert delay > 0 : "Delay is equal to or less than 0!"; if (dropped) { Event.scheduleWithDelay(delay, receiver, eventInformation, MESSAGE_DROPPED); } else { Event.scheduleWithDelay(delay, receiver, eventInformation, MESSAGE_RECEIVED); } } protected void scheduleSendDone(long timeUntilSendDone) { // not used in this MAC } /** * Use this method to dispatch a broadcast message in your MAC-Layer. It * will take care of energy consumption and scheduling. * * @param eventInfo * an implementation of {@link MacEventInformation} containing * information about the message to send as well as the message * itself. */ protected final long sendBroadcast(MacEventInformation eventInfo) { List txNeighbors = getTopologyView().getNeighbors( getMacAddress()); /* * For each Link we have to check whether the message arrives or not. * There are NO retransmissions of broadcasts, as there are no ACKs of * broadcast either. */ Message msg = eventInfo.getMessage(); long timeToSend = -1; for (MacAddress receiver : txNeighbors) { Link l = getTopologyView() .getLinkBetween(getMacAddress(), receiver); assert !receiver.equals(getMacAddress()); assert l.isConnected(); /* * Broadcasts do not depend on an ACK or CTS by a receiver, so we do * not care about the link back to the sender. */ long thisTimeToSend = tryToSend(eventInfo, l, null); if (timeToSend == -1) { timeToSend = thisTimeToSend; } if (timeToSend != thisTimeToSend) { throw new AssertionError( "Inconsitency in the MAC: a broadcast timeToSend is not constant across all links!"); } } if (timeToSend == -1) { /* * We have no neighbors, but we still sent the message - get the * Broadcast-BW and account the corresponding energy consumption. */ Link selfLink = getTopologyView().getLinkBetween(getMacAddress(), getMacAddress()); timeToSend = getUploadTime(msg, selfLink, true); scheduleReceive(this, eventInfo, timeToSend, true); } if (eventInfo.getMessage() instanceof LinkLayerMessage) { _linkMsgEvent((LinkLayerMessage) eventInfo.getMessage(), getHost(), MessageAnalyzer.Reason.SEND); } getEnergyComponent().send(timeToSend, msg, true); return timeToSend; } /** * Send an unicast message * * @param toSend */ protected final long sendUnicast(MacEventInformation eventInfo) { Message msg = eventInfo.getMessage(); long timeToSend = 0; Link l = getTopologyView().getLinkBetween(getMacAddress(), eventInfo.getReceiver()); if (l.isConnected()) { if (enableErrorControl) { Link reverseLink = getTopologyView().getLinkBetween( eventInfo.getReceiver(), getMacAddress()); timeToSend = tryToSend(eventInfo, l, reverseLink); } else { timeToSend = tryToSend(eventInfo, l, null); } assert timeToSend > 0; getEnergyComponent().send(timeToSend, msg, false); /* * Account for energy consumption due to listening in a * broadcast-medium. This will happen even if the Message was not * delivered due to an asymmetric link. */ if (phy.isBroadcastMedium()) { List neighbors = getTopologyView().getNeighbors( getMacAddress()); /* * saving some calls to equals */ boolean skippedReceiver = false; for (MacAddress neighbor : neighbors) { if (skippedReceiver || neighbor.equals(eventInfo.getReceiver())) { skippedReceiver = true; continue; } assert !neighbor.equals(getMacAddress()); MacLayer macReceiver = getTopologyView().getMac(neighbor); if (macReceiver.isOnline()) { macReceiver.getEnergyComponent().receive(timeToSend, msg, false, false); } } } } else { messageDropped(DropReason.NO_LINK, msg); /* * We have no neighbors! */ eventInfo.arrivedAt(this, true); timeToSend = 1 * Time.MICROSECOND; } return timeToSend; } @Override public final BandwidthImpl getCurrentBandwidth() { return currentBandwidth.getEstimatedBandwidth(); } @Override public final BandwidthImpl getMaxBandwidth() { return maximumBandwidth; } /** * Try to deliver the message over the provided link. If the message is a * broadcast, the BW should be constant across all links (we assume the PHY * selects a fixed, more robust modulation for Broadcasts), as there is no * handshaking. Therefore, Broadcasts have no retransmits. * * This method must be called exactly once for every receiver of a * message (in the unicast case, it will be called only once). The receiver * is determined by the link-object. Access this method via the sendUnicast * and sendBroadcast methods to ensure correct behavior of all callbacks and * events. * * @param eventInfo * containing the Message * @param l * the Link from source to receiver * @param backlink * the Link from receiver to source. This may be null, if you do * not want to take link asymmetries into account. * @return the time it took to send the message (time the radio had to stay * active), which is for example used to account for energy * consumption. This is NOT the same as the time we waited before * scheduling the receive-event at the MAC, as latency on the link * is not included. */ private final long tryToSend(MacEventInformation eventInfo, Link l, Link backlink) { /* * Time the MACs need to operate in HighPower (are active) */ long totalSendingTime = 0; boolean isBroadcast = eventInfo.isBroadcast(); /* * Delay is the time we wait before scheduling the receive-event at the * other MAC. */ long delay = 0; long latency = l.getLatency(); long uploadTime = getUploadTime(eventInfo.getMessage(), l, isBroadcast); /* * The following assertions are to ensure proper working conditions, * enable assertions during development if you extend this MAC. */ assert enableErrorControl && (backlink != null || isBroadcast) || !enableErrorControl; assert l.isConnected(); /* * TODO: virtual MTU-behavior -> if l.getMTU < msgSize, calculate drop * for every virtual MTU to determine, if a packet can be delivered. */ /* * Does a message arrive at the receiver? */ boolean dropped = false; if (enableErrorControl) { /* * Number of Retransmissions (message is sent retransmissions + 1 * times). We do not really send and receive ACKs here, this is up * to more advanced MACs. Instead, assume zero-time ACKs that might * get lost (drop), and if this is the case, we would resend the * message which will add on the total delay. */ int allowedRetransmissions = 0; if (!isBroadcast) { allowedRetransmissions = maxRetransmissions; } int tries = 0; do { if (tries > allowedRetransmissions) { dropped = true; break; } double candidate = random.nextDouble(); dropped = candidate <= l.getDropProbability(); tries++; } while (dropped); /* * Calculate the Retransmits that would occur due to ACKs not being * received by the sender. This is not done for broadcasts. */ if (!dropped && tries <= maxRetransmissions) { int ackTransmits = 0; boolean ackDropped = false; do { if (ackTransmits + tries > allowedRetransmissions) { ackDropped = true; break; } double candidate = random.nextDouble(); ackDropped = candidate <= backlink.getDropProbability(); ackTransmits++; } while (ackDropped); assert (ackDropped && tries + ackTransmits == allowedRetransmissions + 1) || (!ackDropped); tries += ackTransmits; } /* * FIXME: ACK-timing? Do we need to take this into account or do we * just assume zero-time ACKs? */ delay = (latency + uploadTime) * tries; totalSendingTime = uploadTime * tries; } else { /* * Just send. ETHERNET-Style, no error control on lower layers. */ double candidate = random.nextDouble(); dropped = candidate <= l.getDropProbability(); delay = latency + uploadTime; totalSendingTime = uploadTime; } /* * Consume energy at the receiver, even if the message was not * successfully delivered - he had to listen all the time. */ MacLayer macReceiver = getTopologyView().getMac(l.getDestination()); if (macReceiver.isOnline()) { macReceiver.getEnergyComponent().receive(totalSendingTime, eventInfo.getMessage(), eventInfo.isBroadcast(), true); } if (dropped) { /* * Notify Mac, if the message has been dropped due to * MAX_RETRANSMITS. We do not care for dropped Broadcasts */ if (!eventInfo.isBroadcast()) { messageDropped(DropReason.LINK_DROP, eventInfo.getMessage()); } } else { if (!eventInfo.isBroadcast() && eventInfo.getMessage() instanceof LinkLayerMessage) { _linkMsgEvent((LinkLayerMessage) eventInfo.getMessage(), getHost(), MessageAnalyzer.Reason.SEND); } } /* * Schedule MAC-Events even for dropped Messages in order to get the * callback as soon as the current MAC is able to send again. */ scheduleReceive(macReceiver, eventInfo, delay, dropped); currentBandwidth.outgoingTransmission(eventInfo.getMessage().getSize()); /* * Latency is not included, because we only have to stay active until * the message is being sent, not until it is fully delivered. In a * broadcast-scenario tries will always be one. */ assert totalSendingTime > 0; return totalSendingTime; } /** * Calculate the time it takes to upload the message via the given link (if * sending would be possible at the full RawBandwidth of the PHY). A more * advanced MAC will add some coding (making the message larger) and access * control scheme for multiple transmissions. * * This time is later used to account for energy consumption at sender and * receiver. * * @param msg * @param l * @return */ protected long getUploadTime(Message msg, Link l, boolean isBroadcast) { // Size is in byte, bandwidth in bit/s long uploadTime = Math.max( msg.getSize() * 8 * Time.SECOND / l.getBandwidth(isBroadcast), Time.MICROSECOND); assert uploadTime > 0; return uploadTime; } @Override public PhyType getPhyType() { return phy; } /** * Overwrite this method to implement additional event handling, if needed. * * @param se */ protected void handleEvent(Object data, int type) { // not needed here. } @Override public final void eventOccurred(Object data, int type) { if (type == MESSAGE_RECEIVED) { if (data instanceof MacEventInformation) { MacEventInformation meInfo = (MacEventInformation) data; meInfo.arrivedAt(this, !isOnline()); if (isOnline()) { currentBandwidth.incomingTransmission(meInfo.getMessage() .getSize()); this.handleReceivedMessage(meInfo); } else { // receiving mac (our mac) is offline if (!meInfo.isBroadcast()) { this.messageDropped(DropReason.RECEIVING_MAC_OFFLINE, meInfo.getMessage()); } } } else { throw new AssertionError( "A SimulationEvents getData() in the MAC has to return a MacEventInformation!"); } } else if (type == MESSAGE_DROPPED) { /* * We use MESSAGE_DROPPED to schedule events for messages that are * dropped. This helps us to send messages "one after the other" by * using a callback from the receiving MAC even if the message was * not transmitted. */ if (data instanceof MacEventInformation) { MacEventInformation meInfo = (MacEventInformation) data; meInfo.arrivedAt(this, true); } else { throw new AssertionError( "A SimulationEvents getData() in the MAC has to return a MacEventInformation!"); } } handleEvent(data, type); } @Override public boolean isOnline() { return isOnline; } @Override public void goOffline() { energyComponent.turnOff(); isOnline = false; /* * TODO Maybe its cooler to let the entries timeout instead... * Suggestions? */ // drop messages in queue Iterator it = queue.iterator(); while (it.hasNext()) { QueueEntry entry = it.next(); it.remove(); messageDropped(DropReason.SENDING_MAC_OFFLINE, entry.getMessage()); } } @Override public void goOnline() { /* * Only go online if the battery still permits it */ if (energyComponent.turnOn()) { isOnline = true; } } @Override public final void send(MacAddress receiver, LinkLayerMessage message) { if (!isOnline()) { // we are offline messageDropped(DropReason.SENDING_MAC_OFFLINE, message); } else { // clean up the queue removeOutdatedQueueEntries(); if (maxQueueLength > 0 && queue.size() >= maxQueueLength) { // queue is full, even after clean up messageDropped(DropReason.QUEUE_FULL, message); // FIXME BR DEBUG Print it // int idx = 0; // for (QueueEntry qe : queue) { // Message msg = qe.getMessage(); // while (msg.getPayload() != null) { // msg = msg.getPayload(); // } // System.out.println(idx + " " // + msg.getClass().getSimpleName() + " " // + msg.toString()); // idx++; // } } else { // still a spot in the queue, add message and notify MAC queue.add(new QueueEntry(receiver, message)); handleNewQueueEntry(); } } } @Override public final MacAddress getMacAddress() { return macAddress; } @Override public NetID getNetId() { if (netID == null) { // Wow... :) netID = host.getNetworkComponent().getByName(getPhyType().getNetInterfaceName()).getLocalInetAddress(); if (netID == null) { throw new AssertionError(); } } return netID; } @Override public final void setMessageListener(LinkMessageListener listener) { this.messageListener = listener; } @Override public final SimHost getHost() { return host; } @Override public String toString() { return "MacLayer: " + macAddress.toString() + " " + phy.toString(); } /** * An entry in the Message queue of the MAC * * @author Bjoern Richerzhagen * @version 1.0, 21.02.2012 */ public class QueueEntry { /** * The timestamp when the message was added to the queue */ private long timeEntered; private MacAddress receiver; private LinkLayerMessage message; public QueueEntry(MacAddress receiver, LinkLayerMessage message) { this.receiver = receiver; this.message = message; this.timeEntered = Time.getCurrentTime(); } public LinkLayerMessage getMessage() { return message; } public MacAddress getReceiver() { return receiver; } public long getTimeEntered() { return timeEntered; } } /** * A Stub for an {@link EnergyCommunicationComponent} to allow simulations * without an energy model. Otherwise we would have to flood the code with * if == null statements. * * @author Bjoern Richerzhagen * @version 1.0, 27.02.2012 */ private class EnergyComponentStub implements EnergyCommunicationComponent { private PhyType phy; private boolean on = false; public EnergyComponentStub(PhyType phy) { this.phy = phy; } @Override public ComponentType getType() { return ComponentType.COMMUNICATION; } @Override public void setEnergyEventListener(EnergyEventListener listener) { // not interested } @Override public void eventOccurred(Object content, int type) { // will not happen } @Override public PhyType getPhyType() { return phy; } @Override public void send(long duration, Message msg, boolean isBrodcast) { // nothing to do } @Override public void receive(long duration, Message msg, boolean isBroadcast, boolean isIntendedReceiver) { // nothing to do } @Override public boolean turnOff() { on = false; return true; } public boolean turnOn() { on = true; return true; } @Override public boolean isOn() { return on; } @Override public void doFakeStateChange() { // TODO Auto-generated method stub } } /** * A Live-Analyzer that aggregates Message Drop reasons inside the MAC. * * @author Bjoern Richerzhagen * @version 1.0, 26.03.2012 */ public class MacMessagesDropReasonProgress implements ProgressValue { public long[] _messageDropCounters; public final String name = "Link " + getPhyType().toString() + " DropReason"; public MacMessagesDropReasonProgress() { _messageDropCounters = new long[DropReason.values().length]; } @Override public String getName() { return name; } public void increment(DropReason reason) { _messageDropCounters[reason.ordinal()]++; } @Override public String getValue() { StringBuffer out = new StringBuffer(); for (DropReason reason : DropReason.values()) { out.append(reason.toString()); out.append(": "); out.append(_messageDropCounters[reason.ordinal()]); out.append("\n"); } return out.toString(); } } private static boolean _linkAnalyzerInitialized = false; private static LinklayerAnalyzer _linkAnalyzer = null; protected static void _linkMsgEvent(LinkLayerMessage msg, SimHost host, Reason reason) { if (!_linkAnalyzerInitialized) { try { _linkAnalyzer = Monitor.get(LinklayerAnalyzer.class); } catch (AnalyzerNotAvailableException e) { _linkAnalyzer = null; } _linkAnalyzerInitialized = true; } if (_linkAnalyzerInitialized && _linkAnalyzer != null) { _linkAnalyzer.linkMsgEvent(msg, host, reason); } } }