/* * 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.topology.views; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import de.tud.kom.p2psim.api.linklayer.LinkLayer; 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.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.TopologyComponent; import de.tud.kom.p2psim.api.topology.movement.MovementSupported; import de.tud.kom.p2psim.api.topology.views.DropProbabilityDeterminator; import de.tud.kom.p2psim.api.topology.views.LatencyDeterminator; import de.tud.kom.p2psim.api.topology.views.TopologyView; import de.tud.kom.p2psim.impl.topology.util.PositionVector; import de.tudarmstadt.maki.simonstrator.api.Binder; import de.tudarmstadt.maki.simonstrator.api.Host; import de.tudarmstadt.maki.simonstrator.api.Monitor; import de.tudarmstadt.maki.simonstrator.api.Monitor.Level; import de.tudarmstadt.maki.simonstrator.api.Rate; import de.tudarmstadt.maki.simonstrator.api.Time; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location; /** * To ease implementation of new {@link TopologyView}s, this class provides * common methods for all topologies. It can be configured to support movement * and/or obstacles and will then provide a lot more convenient caching * functionalities. * * @author Bjoern Richerzhagen * @version 1.0, 06.03.2012 */ public abstract class AbstractTopologyView implements TopologyView { private PhyType phy; /** * An object that provides the latencies for each link. */ private LatencyDeterminator latencyDeterminator; /** * An object that provides the drop rates for a link. */ private DropProbabilityDeterminator dropProbabilityDeterminator; /** * All registered MACs */ private Map macs = new HashMap(); /** * This is used to cache all Link-Objects. Rather than destroying and * recreating the Link-Object, its properties should be modified if the * topology changes. */ private Map> linkCache = new HashMap>(); /** * A cache for the neighbors of a mac (unification of TX and RX) */ private Map> neighborsCache = new HashMap>(); /** * A marker for outdated neighborhoods (only used on movementSupported * topologies) */ private Map neighborsOutdated; /** * An access-list for the Positions of hosts (the objects will be updated by * the MovementModels "automagically") */ private Map positions = new HashMap(); /** * Indicating that this View supports movement. This will enable some * additional caching mechanisms that would be useless if movement is not * used. */ private boolean movementSupported = false; /** * Indicating, that this view is targeted towards the simulations of real * link layers. See {@link TopologyView}. */ private boolean hasRealLinkLayer = false; /** * Basic TopologyView, does not support movement * * @param phy */ public AbstractTopologyView(PhyType phy) { this(phy, false); } /** * A TopologyView that can be used to support movement/obstacles * * @param phy * @param movementSupported * @param obstaclesSupported */ public AbstractTopologyView(PhyType phy, boolean movementSupported) { Binder.registerComponent(this); this.phy = phy; this.movementSupported = movementSupported; if (movementSupported) { neighborsOutdated = new HashMap(); } } /** * For the XML-configurable versions * * @param phy */ public void setPhy(String phy) { phy = phy.toUpperCase(); try { this.phy = PhyType.valueOf(phy); } catch (IllegalArgumentException e) { throw new ConfigurationException("The PHY " + phy + " is unknown. Please select one of " + PhyType.printTypes()); } if (this.phy == null) { throw new ConfigurationException("The PHY " + phy + " is unknown. Please select one of " + PhyType.printTypes()); } } @Override public final PhyType getPhyType() { return phy; } /** * This object determines the latency on a link. If it is not set, the * default value defined by the PHY will be used. * * @param latencyDeterminator */ public void setLatency(LatencyDeterminator latencyDeterminator) { this.latencyDeterminator = latencyDeterminator; } /** * This object determines the drop rate (packet loss) on a link. If it is * not set, the default value defined by the PHY will be used. * * @param dropRateDeterminator */ public void setDropRate(DropProbabilityDeterminator dropRateDeterminator) { this.dropProbabilityDeterminator = dropRateDeterminator; } /** * Access the {@link LatencyDeterminator} of this View. If no * {@link LatencyDeterminator} is configured, this will return the latency * of the PHY. * * @return */ protected LatencyDeterminator getLatencyDeterminator() { if (latencyDeterminator == null) { latencyDeterminator = new LatencyDeterminator() { @Override public void onMacAdded(MacLayer mac, TopologyView viewParent) { // } @Override public long getLatency(TopologyView view, MacAddress source, MacAddress destination, Link link) { return getPhyType().getDefaultLatency(); } }; } return latencyDeterminator; } /** * Access the {@link LatencyDeterminator} of this View. If no * {@link LatencyDeterminator} is configured, this will return the latency * of the PHY. * * @return */ protected DropProbabilityDeterminator getDropProbabilityDeterminator() { if (dropProbabilityDeterminator == null) { dropProbabilityDeterminator = new DropProbabilityDeterminator() { @Override public void onMacAdded(MacLayer mac, TopologyView viewParent) { // } @Override public double getDropProbability(TopologyView view, MacAddress source, MacAddress destination, Link link) { return getPhyType().getDefaultDropProbability(); } }; } return dropProbabilityDeterminator; } /** * Default Bandwidth determination for a link: the minimum of the sources * uplink and the destinations downlink. * * @param source * @param destination * @return bandwidth in bit/s {@link Rate} */ protected long determineLinkBandwidth(MacAddress source, MacAddress destination) { BandwidthImpl sourceBandwidth = getMac(source).getMaxBandwidth(); BandwidthImpl destinationBandwidth = getMac(destination).getMaxBandwidth(); return Math.min(sourceBandwidth.getUpBW(), destinationBandwidth.getDownBW()); } /** * Latency determination for a link. This default implementation just asks * the {@link LatencyDeterminator}. * * @param source * @param destination * @return */ protected long determineLinkLatency(MacAddress source, MacAddress destination) { return getLatencyDeterminator().getLatency(this, source, destination, null); } /** * Probability that a message is lost on this link. * * @param source * @param destination * @return */ protected double determineLinkDropProbability(MacAddress source, MacAddress destination) { return dropProbabilityDeterminator.getDropProbability(this, source, destination, null); } @Override public final void addedComponent(TopologyComponent comp) { LinkLayer ll = comp.getHost().getLinkLayer(); if( ll == null ) { /* No linklayer specified in config. */ Monitor.log(AbstractTopologyView.class, Level.WARN, "No LinkLayer specified. Cannot add Topology."); return; } if (ll.hasPhy(phy)) { /* * Collect all hosts that are part of this View */ MacLayer mac = comp.getHost().getLinkLayer().getMac(phy); macs.put(mac.getMacAddress(), mac); addedMac(mac); /* * Initialize all cache-maps */ linkCache.put(mac.getMacAddress(), new HashMap()); if (movementSupported) { neighborsOutdated.put(mac.getMacAddress(), true); } positions.put(mac.getMacAddress(), comp.getHost() .getTopologyComponent().getRealPosition()); getLatencyDeterminator().onMacAdded(mac, this); getDropProbabilityDeterminator().onMacAdded(mac, this); } } @Override public MacLayer getMac(MacAddress address) { return macs.get(address); } @Override public Collection getAllMacs() { return macs.values(); } long timeLastMovement = 0; @Override public void onLocationChanged(Host host, Location location) { if (Time.getCurrentTime() != timeLastMovement) { timeLastMovement = Time.getCurrentTime(); /* * again, topologies might or might not support movement. We do not * force handling of this callback. The default implementation does * nothing. If a topology uses this callback is should mark * neighborhoods as outdated and re-calculate them on-demand as soon as * the hosts first requests the neighborhood again. */ if (movementSupported) { /* * mark all neighborhoods as outdated */ for (Entry entry : neighborsOutdated .entrySet()) { entry.setValue(true); } } } } @Override public final L getLinkBetween(MacAddress source, MacAddress destination) { return getCachedLink(source, destination); } @Override public final List getNeighbors(MacAddress address) { return Collections.unmodifiableList(getCachedNeighborhood(address)); } /* * Methods to be implemented by Views */ /** * This is called as soon as a new component is added to the Topology to * allow the extending view to perform more advanced caching such as * pre-calculation of neighborhoods and links in a fixed TopologyView. * * @param mac */ protected abstract void addedMac(MacLayer mac); /** * Called if a Link is outdated and was requested from cache. After this * call the outdated-Flag will be set to false by the * {@link AbstractTopologyView}. This will only be called if the View is * movementSupported! * * @param link */ protected abstract void updateOutdatedLink(L link); /** * Called, if a Link is requested that has not been added to the cache * already. You have to return a Link extending {@link DefaultLink}. If your * View is movementSupported, updateOutdatedLink() will be called right * after this method. ALWAYS return a valid Link-Object. If two hosts * are not connected, you have to make sure that Link.isConnected() returns * false. * * @param source * @param destination * @return */ protected abstract L createLink(MacAddress source, MacAddress destination); /** * Calculate an updated Neighborhood for the provided Source. This is called * if no Neighborhood for a node is found in the cache or if your view is * movementSupported and the neighborhood is outdated due to movement. * * @param neighborhood */ protected abstract List updateNeighborhood(MacAddress source); /* * Caching-Methods */ /** * Get a previously cached Link-Object. Rather than creating new objects you * should update the properties of the cached object instead to speed up * simulation. If there is no Link in the Cache, createLink is called and * the newly created link is added to the cache and returned * * @param source * @param destination * @return the Link */ private L getCachedLink(MacAddress source, MacAddress destination) { L link = linkCache.get(source).get(destination); if (link == null) { link = createLink(source, destination); assert link != null && source != null && destination != null : "Error!"; linkCache.get(source).put(destination, link); if (movementSupported) { // mark as outdated, in order to trigger updateLink next! // linksOutdated.put(link, true); link.setOutdated(true); } } if (movementSupported && link.isOutdated()) { updateOutdatedLink(link); link.setOutdated(false); } return link; } /** * Return the cached neighborhood for the source. This will call * updateNeighborhood if there is no neighborhood in the cache or the * neighborhood is outdated after a movement-operation. * * @param source * @return */ private List getCachedNeighborhood(MacAddress source) { if (movementSupported && neighborsOutdated.get(source) || !neighborsCache.containsKey(source)) { neighborsCache.put(source, updateNeighborhood(source)); if (movementSupported) { neighborsOutdated.put(source, false); } } return neighborsCache.get(source); } /** * If your topology is able to calculate neighborhoods in advance (ie. in a * fixed topology) you might want to use this method to fill the cache * rather than rely on the updateNeighborhood-call for on-demand * calculation. * * @param source * @param neighbors */ protected void setCachedNeighborhood(MacAddress source, List neighbors) { neighborsCache.put(source, neighbors); if (movementSupported) { neighborsOutdated.put(source, false); } } /** * Not really a cache but a hashMap-Lookup for the real position object * * @param source * @return */ protected PositionVector getCachedPosition(MacAddress source) { return positions.get(source); } @Override public Location getPosition(MacAddress address) { return getCachedPosition(address); } @Override public double getDistance(MacAddress addressA, MacAddress addressB) { return getCachedPosition(addressA) .distanceTo(getCachedPosition(addressB)); } @Override public boolean hasRealLinkLayer() { return hasRealLinkLayer; } /** * Mark that this {@link TopologyView} has a real link layer (latencies and * drop rates are Layer 2 measurements!) * * @param hasRealLinkLayer */ public void setHasRealLinkLayer(boolean hasRealLinkLayer) { this.hasRealLinkLayer = hasRealLinkLayer; } }