AbstractTopologyView.java 14.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/*
 * 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 <http://www.gnu.org/licenses/>.
 *
 */

package de.tud.kom.p2psim.impl.topology.views;

23
import java.util.Collection;
24
25
26
27
28
29
30
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;
31
import de.tud.kom.p2psim.api.linklayer.mac.Link;
32
33
34
35
36
37
38
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;
39
import de.tud.kom.p2psim.api.topology.views.DropProbabilityDeterminator;
40
41
import de.tud.kom.p2psim.api.topology.views.LatencyDeterminator;
import de.tud.kom.p2psim.api.topology.views.TopologyView;
42
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
43
import de.tudarmstadt.maki.simonstrator.api.Binder;
44
import de.tudarmstadt.maki.simonstrator.api.Host;
45
46
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Monitor.Level;
47
import de.tudarmstadt.maki.simonstrator.api.Rate;
48
import de.tudarmstadt.maki.simonstrator.api.Time;
49
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location;
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72

/**
 * 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<L extends DefaultLink> 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.
	 */
73
	private DropProbabilityDeterminator dropProbabilityDeterminator;
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110

	/**
	 * All registered MACs
	 */
	private Map<MacAddress, MacLayer> macs = new HashMap<MacAddress, MacLayer>();

	/**
	 * 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<MacAddress, Map<MacAddress, L>> linkCache = new HashMap<MacAddress, Map<MacAddress, L>>();

	/**
	 * A cache for the neighbors of a mac (unification of TX and RX)
	 */
	private Map<MacAddress, List<MacAddress>> neighborsCache = new HashMap<MacAddress, List<MacAddress>>();

	/**
	 * A marker for outdated neighborhoods (only used on movementSupported
	 * topologies)
	 */
	private Map<MacAddress, Boolean> neighborsOutdated;

	/**
	 * An access-list for the Positions of hosts (the objects will be updated by
	 * the MovementModels "automagically")
	 */
	private Map<MacAddress, PositionVector> positions = new HashMap<MacAddress, PositionVector>();

	/**
	 * 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;

111
112
113
114
115
116
	/**
	 * Indicating, that this view is targeted towards the simulations of real
	 * link layers. See {@link TopologyView}.
	 */
	private boolean hasRealLinkLayer = false;

117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
	/**
	 * 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) {
134
		Binder.registerComponent(this);
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
		this.phy = phy;
		this.movementSupported = movementSupported;
		if (movementSupported) {
			neighborsOutdated = new HashMap<MacAddress, Boolean>();
		}
	}

	/**
	 * 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
	 */
184
185
	public void setDropRate(DropProbabilityDeterminator dropRateDeterminator) {
		this.dropProbabilityDeterminator = dropRateDeterminator;
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
	}
	
	/**
	 * 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,
206
						MacAddress destination, Link link) {
207
208
209
210
211
212
213
					return getPhyType().getDefaultLatency();
				}
			};
		}
		return latencyDeterminator;
	}

214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
	/**
	 * 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;
	}

240
241
242
243
244
245
	/**
	 * Default Bandwidth determination for a link: the minimum of the sources
	 * uplink and the destinations downlink.
	 * 
	 * @param source
	 * @param destination
246
	 * @return bandwidth in bit/s {@link Rate}
247
248
249
250
251
	 */
	protected long determineLinkBandwidth(MacAddress source,
			MacAddress destination) {
		BandwidthImpl sourceBandwidth = getMac(source).getMaxBandwidth();
		BandwidthImpl destinationBandwidth = getMac(destination).getMaxBandwidth();
252
		return Math.min(sourceBandwidth.getUpBW(),
253
254
255
256
257
258
259
260
261
262
263
264
265
				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) {
266
267
		return getLatencyDeterminator().getLatency(this, source, destination,
				null);
268
269
270
271
272
273
274
275
276
277
278
	}

	/**
	 * Probability that a message is lost on this link.
	 * 
	 * @param source
	 * @param destination
	 * @return
	 */
	protected double determineLinkDropProbability(MacAddress source,
			MacAddress destination) {
279
280
		return dropProbabilityDeterminator.getDropProbability(this, source,
				destination, null);
281
282
283
284
285
286
287
288
	}

	@Override
	public final void addedComponent(TopologyComponent comp) {
		LinkLayer ll = comp.getHost().getLinkLayer();
		
		if( ll == null ) {
			/* No linklayer specified in config. */
289
290
			Monitor.log(AbstractTopologyView.class, Level.WARN,
					"No LinkLayer specified. Cannot add Topology.");
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
			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<MacAddress, L>());
			if (movementSupported) {
				neighborsOutdated.put(mac.getMacAddress(), true);
			}
			positions.put(mac.getMacAddress(), comp.getHost()
					.getTopologyComponent().getRealPosition());
310
311
			getLatencyDeterminator().onMacAdded(mac, this);
			getDropProbabilityDeterminator().onMacAdded(mac, this);
312
313
314
315
316
317
318
		}
	}

	@Override
	public MacLayer getMac(MacAddress address) {
		return macs.get(address);
	}
319
320
321
322
323
	
	@Override
	public Collection<MacLayer> getAllMacs() {
		return macs.values();
	}
324
325
	
	long timeLastMovement = 0;
326
	
327
	@Override
328
	public void onLocationChanged(Host host, Location location) {
329
330
		if (Time.getCurrentTime() != timeLastMovement) {
			timeLastMovement = Time.getCurrentTime();
331
			/*
332
333
334
335
336
			 * 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.
337
			 */
338
339
340
341
342
343
344
345
			if (movementSupported) {
				/*
				 * mark all neighborhoods as outdated
				 */
				for (Entry<MacAddress, Boolean> entry : neighborsOutdated
						.entrySet()) {
					entry.setValue(true);
				}
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
			}
		}
	}

	@Override
	public final L getLinkBetween(MacAddress source, MacAddress destination) {
		return getCachedLink(source, destination);
	}

	@Override
	public final List<MacAddress> 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 <i>outdated</i>-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. <b>ALWAYS</b> 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<MacAddress> 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<MacAddress> 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<MacAddress> 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
486
	public Location getPosition(MacAddress address) {
487
488
489
490
491
		return getCachedPosition(address);
	}

	@Override
	public double getDistance(MacAddress addressA, MacAddress addressB) {
492
493
		return getCachedPosition(addressA)
				.distanceTo(getCachedPosition(addressB));
494
	}
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509

	@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;
	}
510
}