AbstractTopologyView.java 14 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
42
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.PositionVector;
43
44
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Monitor.Level;
45
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

/**
 * 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.
	 */
69
	private DropProbabilityDeterminator dropProbabilityDeterminator;
70
71
72
73
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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

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

	/**
	 * 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) {
		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
	 */
173
174
	public void setDropRate(DropProbabilityDeterminator dropRateDeterminator) {
		this.dropProbabilityDeterminator = dropRateDeterminator;
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
	}
	
	/**
	 * 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,
195
						MacAddress destination, Link link) {
196
197
198
199
200
201
202
					return getPhyType().getDefaultLatency();
				}
			};
		}
		return latencyDeterminator;
	}

203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
	/**
	 * 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;
	}

229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
	/**
	 * Default Bandwidth determination for a link: the minimum of the sources
	 * uplink and the destinations downlink.
	 * 
	 * @param source
	 * @param destination
	 * @return bandwidth in byte/s
	 */
	protected long determineLinkBandwidth(MacAddress source,
			MacAddress destination) {
		BandwidthImpl sourceBandwidth = getMac(source).getMaxBandwidth();
		BandwidthImpl destinationBandwidth = getMac(destination).getMaxBandwidth();
		return (long) 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) {
255
256
		return getLatencyDeterminator().getLatency(this, source, destination,
				null);
257
258
259
260
261
262
263
264
265
266
267
	}

	/**
	 * Probability that a message is lost on this link.
	 * 
	 * @param source
	 * @param destination
	 * @return
	 */
	protected double determineLinkDropProbability(MacAddress source,
			MacAddress destination) {
268
269
		return dropProbabilityDeterminator.getDropProbability(this, source,
				destination, null);
270
271
272
273
274
275
276
277
	}

	@Override
	public final void addedComponent(TopologyComponent comp) {
		LinkLayer ll = comp.getHost().getLinkLayer();
		
		if( ll == null ) {
			/* No linklayer specified in config. */
278
279
			Monitor.log(AbstractTopologyView.class, Level.WARN,
					"No LinkLayer specified. Cannot add Topology.");
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
			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());
299
300
			getLatencyDeterminator().onMacAdded(mac, this);
			getDropProbabilityDeterminator().onMacAdded(mac, this);
301
302
303
304
305
306
307
		}
	}

	@Override
	public MacLayer getMac(MacAddress address) {
		return macs.get(address);
	}
308
309
310
311
312
	
	@Override
	public Collection<MacLayer> getAllMacs() {
		return macs.values();
	}
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
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

	@Override
	public void afterComponentsMoved() {
		/*
		 * 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<MacAddress, Boolean> entry : neighborsOutdated
					.entrySet()) {
				entry.setValue(true);
			}
		}
	}

	@Override
	public void afterComponentMoved(MovementSupported comp) {
		/*
		 * don't care. One might optimize handling here when compared to a
		 * global recalculation.
		 */
	}

	@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
478
	public Location getPosition(MacAddress address) {
479
480
481
482
483
		return getCachedPosition(address);
	}

	@Override
	public double getDistance(MacAddress addressA, MacAddress addressB) {
484
485
		return getCachedPosition(addressA)
				.distanceTo(getCachedPosition(addressB));
486
487
	}
}