DefaultTopologyComponent.java 19.1 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
23
/*
 * 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;

import java.util.LinkedHashMap;
24
import java.util.LinkedList;
25
import java.util.List;
26
27
28
29
30
31
import java.util.Map;
import java.util.Random;
import java.util.Set;

import de.tud.kom.p2psim.api.common.HostProperties;
import de.tud.kom.p2psim.api.common.SimHost;
32
33
34
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;
35
36
import de.tud.kom.p2psim.api.topology.Topology;
import de.tud.kom.p2psim.api.topology.TopologyComponent;
37
import de.tud.kom.p2psim.api.topology.movement.MovementModel;
38
import de.tud.kom.p2psim.api.topology.placement.PlacementModel;
39
import de.tud.kom.p2psim.api.topology.views.TopologyView;
40
41
import de.tud.kom.p2psim.impl.simengine.Simulator;
import de.tud.kom.p2psim.impl.topology.movement.modular.attraction.AttractionPoint;
42
43
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
44
45
import de.tudarmstadt.maki.simonstrator.api.Graphs;
import de.tudarmstadt.maki.simonstrator.api.Host;
46
import de.tudarmstadt.maki.simonstrator.api.Oracle;
47
import de.tudarmstadt.maki.simonstrator.api.Randoms;
48
import de.tudarmstadt.maki.simonstrator.api.Time;
49
import de.tudarmstadt.maki.simonstrator.api.common.graph.Graph;
Björn Richerzhagen's avatar
Björn Richerzhagen committed
50
51
import de.tudarmstadt.maki.simonstrator.api.common.graph.IEdge;
import de.tudarmstadt.maki.simonstrator.api.common.graph.INode;
52
import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID;
53
import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException;
54
55
import de.tudarmstadt.maki.simonstrator.api.component.network.NetInterface;
import de.tudarmstadt.maki.simonstrator.api.component.network.NetworkComponent.NetInterfaceName;
56
57
58
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.LocationListener;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.LocationRequest;
59
60
61
62
63
import de.tudarmstadt.maki.simonstrator.api.component.sis.SiSComponent;
import de.tudarmstadt.maki.simonstrator.api.component.sis.SiSDataCallback;
import de.tudarmstadt.maki.simonstrator.api.component.sis.SiSInfoProperties;
import de.tudarmstadt.maki.simonstrator.api.component.sis.SiSInformationProvider.SiSProviderHandle;
import de.tudarmstadt.maki.simonstrator.api.component.sis.exception.InformationNotAvailableException;
Björn Richerzhagen's avatar
Björn Richerzhagen committed
64
import de.tudarmstadt.maki.simonstrator.api.component.sis.type.SiSTypes;
65
import de.tudarmstadt.maki.simonstrator.api.component.sis.util.SiSTopologyProvider;
66
67
import de.tudarmstadt.maki.simonstrator.api.component.topology.TopologyID;
import de.tudarmstadt.maki.simonstrator.api.component.transport.ConnectivityListener;
68
69
70
71
72
73
74

/**
 * Default implementation of a {@link TopologyComponent}.
 * 
 * @author Bjoern Richerzhagen
 * @version 1.0, 29.02.2012
 */
75
76
public class DefaultTopologyComponent implements TopologyComponent {

77
78
79
80
	protected static Random rnd = Randoms.getRandom(AttractionPoint.class);

	private SimHost host;

81
	private final PositionVector position;
82
83
84

	private Topology topology;

85
	private double currentMovementSpeed = -1;
86

87
88
89
	private Map<LocationListener, LocationRequestImpl> openRequests = new LinkedHashMap<LocationListener, LocationRequestImpl>();

	private List<LocationListener> listeners = new LinkedList<>();
90

91
	private MovementModel movementModel;
92

93
94
	private PlacementModel placementModel;

95
	/**
96
97
98
99
100
	 * Create a TopologyComponent for the current host.
	 * 
	 * @param host
	 * @param topology
	 * @param movementModel
101
102
	 */
	public DefaultTopologyComponent(SimHost host, Topology topology,
103
			MovementModel movementModel, PlacementModel placementModel) {
104
105
		this.topology = topology;
		this.host = host;
106
		this.position = new PositionVector(0, 0);
107

108
109
110
111
		this.movementModel = movementModel;
		if (this.movementModel != null) {
			this.movementModel.addComponent(this);
		}
112

113
114
115
116
		this.placementModel = placementModel;
		if (this.placementModel != null) {
			this.placementModel.addComponent(this);
		}
117
118
119
120
	}

	@Override
	public void initialize() {
121
122
123
124
		/*
		 * Set the component's initial position and notify listeners of the
		 * Topology that this component is initialized.
		 */
125
		topology.addComponent(this);
126
		movementModel.placeComponent(this);
127

128
129
130
131
132
133
		if (placementModel != null) {
			/*
			 * Legacy support for placement models.
			 */
			position.set(placementModel.place(this));
		}
134

135
		try {
136
137
138
139
			final SiSComponent sis = host.getComponent(SiSComponent.class);
			sis.provide().nodeState(SiSTypes.PHY_LOCATION,
					new SiSDataCallback<Location>() {

140
141
						Set<INodeID> localID = INodeID
								.getSingleIDSet(getHost().getId());
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

						@Override
						public Location getValue(INodeID nodeID,
								SiSProviderHandle providerHandle)
								throws InformationNotAvailableException {
							if (nodeID.equals(getHost().getId())) {
								return getLastLocation();
							} else {
								throw new InformationNotAvailableException();
							}
						}

						@Override
						public Set<INodeID> getObservedNodes() {
							return localID;
						}

						@Override
						public SiSInfoProperties getInfoProperties() {
							return new SiSInfoProperties();
						}
					});

			// Provide Underlay topology
			Event.scheduleImmediately(new EventHandler() {

				@Override
				public void eventOccurred(Object content, int type) {
170
					if (getHost().getLinkLayer().hasPhy(PhyType.WIFI)) {
171
						new SiSTopologyProvider(sis, SiSTypes.NEIGHBORS_WIFI,
172
173
								DefaultTopologyComponent.this,
								getTopologyID(NetInterfaceName.WIFI, true),
174
175
								DefaultTopologyComponent.class);
					}
176
				}
177
178
			}, null, 0);

179
180
181
		} catch (ComponentNotAvailableException e) {
			// OK
		}
182
183
184
185
186
187
	}

	@Override
	public void shutdown() {
		topology = null;
		host = null;
188
		movementModel = null;
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
	}

	@Override
	public SimHost getHost() {
		return host;
	}

	@Override
	public PositionVector getRealPosition() {
		return position;
	}

	@Override
	public Topology getTopology() {
		return topology;
	}
205
206
207
208
209
210
211
212
	
	/**
	 * Access to the movement model
	 * @return
	 */
	public MovementModel getMovementModel() {
		return movementModel;
	}
213
214
215
216
217
218
219
220
221
222
223
224
225

	@Override
	public double getMinMovementSpeed() {
		HostProperties properties = getHost().getProperties();

		return properties.getMinMovementSpeed();
	}

	@Override
	public double getMaxMovementSpeed() {
		HostProperties properties = getHost().getProperties();
		return properties.getMaxMovementSpeed();
	}
226

227
228
229
230
231
	private void calcRandomMovementSpeed() {
		double min_speed = getMinMovementSpeed();
		double max_speed = getMaxMovementSpeed();

		double value = rnd.nextDouble();
232
233
		this.currentMovementSpeed = (value * (max_speed - min_speed))
				+ min_speed;
234
	}
235
236
237

	@Override
	public double getMovementSpeed() {
238
239
240
241
242
		if (currentMovementSpeed == -1) {
			calcRandomMovementSpeed();
		}
		return this.currentMovementSpeed;
	}
243

244
245
246
	@Override
	public void setMovementSpeed(double speed) {
		this.currentMovementSpeed = speed;
247
248
249
250
	}

	@Override
	public Location getLastLocation() {
251
252
253
254
255
		/*
		 * As we want to mimic real world behavior, the current position
		 * snapshot is cloned to prevent information propagation due to Java.
		 */
		return position.clone();
256
257
	}

258
259
260
261
262
263
264
265
	@Override
	public void updateCurrentLocation(double longitude, double latitude) {
		position.setEntries(longitude, latitude);
		// notify "non-request" listeners
		for (LocationListener locationListener : listeners) {
			locationListener.onLocationChanged(getHost(), getLastLocation());
		}
	}
266

267
268
269
270
	@Override
	public void setNewTargetLocation(double longitude, double latitude) {
		movementModel.changeTargetLocation(this, longitude, latitude);
	}
271

272
273
274
	@Override
	public void requestLocationUpdates(LocationRequest request,
			LocationListener listener) {
275
		if (openRequests.containsKey(listener)) {
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
			throw new AssertionError(
					"This LocationListener is already in use.");
		}
		if (request == null) {
			/*
			 * This listener wants to be triggered on EVERY position update, but
			 * it does not want to request position updates.
			 */
			if (!listeners.contains(listener)) {
				listeners.add(listener);
			}
		} else {
			/*
			 * Listener has its own request timing.
			 */
			LocationRequestImpl req = (LocationRequestImpl) request;
			openRequests.put(listener, req);
			req.immunizeAndStart(listener);
294
		}
295
296
297
298
	}

	@Override
	public void removeLocationUpdates(LocationListener listener) {
299
		listeners.remove(listener);
300
		LocationRequestImpl impl = openRequests.remove(listener);
301
302
303
		if (impl != null) {
			impl.cancel(listener);
		}
304
305
306
307
308
309
310
	}

	@Override
	public LocationRequest getLocationRequest() {
		return new LocationRequestImpl();
	}

311
	/**
312
313
	 * Update 15.03.16 added support for multiple listeners (however, frequency
	 * etc. is immune after the first request is registered.)
314
315
316
317
	 * 
	 * @author Bjoern Richerzhagen
	 * @version 1.0, Mar 15, 2016
	 */
318
	private class LocationRequestImpl implements LocationRequest, EventHandler {
319
320
321
322
323
324

		private boolean immune = false;

		private long interval = 1 * Simulator.MINUTE_UNIT;

		private int priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY;
325

326
		private Location lastLocation = null;
327

328
		private List<LocationListener> listeners = new LinkedList<LocationListener>();
329
330
		
		private int eventTypeSeq = 0;
331

332
333
334
335
		public LocationRequestImpl() {
			// nothing to do
		}

336
337
		protected void cancel(LocationListener listener) {
			boolean removed = listeners.remove(listener);
338
339
340
341
			if (listeners.isEmpty()) {
				// upcoming event is no longer valid!
				eventTypeSeq++;
			}
342
			assert removed;
343
344
345
346
347
		}

		protected void immunizeAndStart(LocationListener listener) {
			immune = true;
			assert interval > 0;
348
349
			if (listeners.isEmpty()) {
				// Only start once!
350
				lastLocation = null;
351
				Event.scheduleImmediately(this, null, eventTypeSeq);
352
353
			} else {
				// Fire each new listener at least once
354
				listener.onLocationChanged(getHost(), getLastLocation());
355
			}
356
			listeners.add(listener);
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
		}

		@Override
		public void setInterval(long interval) {
			if (!immune) {
				this.interval = interval;
			}
		}

		@Override
		public void setPriority(int priority) {
			if (!immune) {
				this.priority = priority;
			}
		}

373
374
		@Override
		public void eventOccurred(Object content, int type) {
375
376
377
378
379
380
381
382
383
			if (eventTypeSeq != type) {
				/*
				 * Discard invalid events caused when a client cancels updates 
				 * but reactivates the request within the update frequency 
				 * interval. In this case, the old events continue rescheduling 
				 * themselves.
				 */
				return;
			}
384
385
			if (!listeners.isEmpty()) {
				// Only reschedule, if at least one listener is ... listening
386
				Location newLoc = getLastLocation();
387
				listeners.forEach((LocationListener listener) -> listener
388
							.onLocationChanged(getHost(), newLoc));
389
				lastLocation = newLoc;
390
				Event.scheduleWithDelay(interval, this, null, eventTypeSeq);
391
392
393
			}
		}

394
395
	}

396
397
398
399
400
401
402
403
404
405
406
	/*
	 * Methods for the Graph Interface
	 */

	/**
	 * Graph views: static, as we use global knowledge and maintain one shared
	 * graph (potentially with partitions!)
	 */
	private final static LinkedHashMap<TopologyID, LocalGraphView> graphViews = new LinkedHashMap<>();

	@Override
407
408
409
410
	public TopologyID getTopologyID(NetInterfaceName netName,
			boolean onlyOnline) {
		TopologyID id = TopologyID.getIdentifier(
				netName.toString() + (onlyOnline ? "-online" : "-all"),
411
412
413
414
415
416
417
418
419
420
				DefaultTopologyComponent.class);
		if (!this.graphViews.containsKey(id)) {
			this.graphViews.put(id, new LocalGraphView(netName, onlyOnline));
		}
		return id;
	}

	@Override
	public TopologyID getTopologyID(NetInterfaceName netName,
			boolean onlyOnline, double range) {
421
422
423
		TopologyID id = TopologyID.getIdentifier(
				netName.toString() + (onlyOnline ? "-online" : "-all")
						+ String.valueOf(range),
424
425
				DefaultTopologyComponent.class);
		if (!this.graphViews.containsKey(id)) {
426
427
			this.graphViews.put(id,
					new LocalGraphView(netName, onlyOnline, range));
428
429
430
431
432
		}
		return id;
	}

	@Override
Björn Richerzhagen's avatar
Björn Richerzhagen committed
433
	public INode getNode(TopologyID identifier) {
434
435
436
437
438
		assert graphViews.containsKey(identifier);
		return graphViews.get(identifier).getOwnNode(host);
	}

	@Override
Björn Richerzhagen's avatar
Björn Richerzhagen committed
439
	public Set<IEdge> getNeighbors(TopologyID topologyIdentifier) {
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
		assert graphViews.containsKey(topologyIdentifier);
		return graphViews.get(topologyIdentifier).getNeighbors(host);
	}

	@Override
	public Graph getLocalView(TopologyID topologyIdentifier) {
		assert graphViews.containsKey(topologyIdentifier);
		return graphViews.get(topologyIdentifier).getLocalView();
	}

	@Override
	public Iterable<TopologyID> getTopologyIdentifiers() {
		return graphViews.keySet();
	}

	/**
	 * This is calculated based on global knowledge. It only registers as
457
	 * {@link LocationListener}, if a range is specified by the Provider.
458
459
460
461
	 * 
	 * @author Bjoern Richerzhagen
	 * @version 1.0, May 13, 2015
	 */
462
463
	private class LocalGraphView
			implements LocationListener, ConnectivityListener {
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494

		/**
		 * Marker: has there been any movement since the graph view was last
		 * requested? If so: recalculate! Otherwise, we ignore this object to
		 * not perform calculations if no one is interested...
		 */
		private final double distance;

		private final boolean isDistanceBased;

		private final NetInterfaceName medium;

		private final TopologyView topoView;

		private final boolean onlyOnline;

		private Graph currentView;

		private boolean isInvalid = true;

		private final PhyType phy;

		public LocalGraphView(NetInterfaceName medium, boolean onlyOnline) {
			this(medium, onlyOnline, -1);
		}

		public LocalGraphView(NetInterfaceName medium, boolean onlyOnline,
				double distance) {
			this.medium = medium;
			PhyType localPhy = null;
			for (PhyType currPhy : PhyType.values()) {
Björn Richerzhagen's avatar
Björn Richerzhagen committed
495
496
				if (currPhy.getNetInterfaceName() == medium
						&& getTopology().getTopologyView(currPhy) != null) {
497
498
499
500
501
502
503
504
505
506
507
					localPhy = currPhy;
					break;
				}
			}
			phy = localPhy;
			assert localPhy != null;
			this.topoView = getTopology().getTopologyView(localPhy);
			this.distance = distance;
			this.onlyOnline = onlyOnline;
			this.isDistanceBased = (distance > 0);
			assert !isDistanceBased || phy.isBroadcastMedium();
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529

			if (phy.isBroadcastMedium() || onlyOnline) {
				for (Host host : Oracle.getAllHosts()) {
					if (phy.isBroadcastMedium()) {
						try {
							DefaultTopologyComponent dcomp = host.getComponent(
									DefaultTopologyComponent.class);
							dcomp.requestLocationUpdates(null,
									LocalGraphView.this);
						} catch (ComponentNotAvailableException e) {
							continue;
						}
					}
					if (onlyOnline) {
						if (host.getNetworkComponent()
								.getByName(medium) != null) {
							host.getNetworkComponent().getByName(medium)
									.addConnectivityListener(
											LocalGraphView.this);
						}
					}
				}
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
			}
		}

		private void recalculateLocalView() {
			if (!isInvalid) {
				/*
				 * Graphs are invalidated (i) based on movement, IFF a range was
				 * specified, (ii) based on online/offline events, IFF only
				 * online hosts are to be considered.
				 */
				return;
			}
			/*
			 * Calculate a complete global connectivity graph
			 */

			// Create new, empty graph
			currentView = Graphs.createGraph();

			// Add all (online?) nodes
			for (MacLayer mac : topoView.getAllMacs()) {
				if (!onlyOnline || mac.isOnline()) {
Björn Richerzhagen's avatar
Björn Richerzhagen committed
552
					INode node = currentView.createNode(mac.getHost().getId());
553
					node.setProperty(SiSTypes.PHY_LOCATION,
554
							topoView.getPosition(mac.getMacAddress()).clone());
Björn Richerzhagen's avatar
Björn Richerzhagen committed
555
					currentView.addElement(node);
556
557
558
559
560
561
				}
			}

			if (isDistanceBased) {
				// Build neighbors solely based on an assumed range
				for (MacLayer mac : topoView.getAllMacs()) {
562
563
564
565
566
					// Fix Christoph Storm:
					// Do not take offline nodes into account, unless told to do
					// so...
					if (onlyOnline && !currentView
							.containsNode(mac.getHost().getId())) {
Christoph Storm's avatar
Christoph Storm committed
567
568
						continue;
					}
569
570
571
					// Consider all nodes as potential neighbors
					for (MacLayer neighborMac : topoView.getAllMacs()) {
						// create, but do NOT add the node object
572
573
						INode neighbor = currentView
								.createNode(neighborMac.getHost().getId());
574
						// only online nodes (already in graph)
Björn Richerzhagen's avatar
Björn Richerzhagen committed
575
576
						if (!onlyOnline
								|| currentView.containsNode(neighbor.getId())) {
577
578
579
							// Distance?
							if (topoView.getDistance(mac.getMacAddress(),
									neighborMac.getMacAddress()) <= distance) {
580
581
582
								IEdge edge = currentView.createEdge(
										mac.getHost().getId(),
										neighborMac.getHost().getId());
Björn Richerzhagen's avatar
Björn Richerzhagen committed
583
								currentView.addElement(edge);
584
585
586
587
588
589
590
							}
						}
					}
				}
			} else {
				// Build neighborhoods based on underlay neighbors (1-hop)
				for (MacLayer mac : topoView.getAllMacs()) {
Christoph Storm's avatar
Christoph Storm committed
591
					// Fix Christoph Storm:
592
593
594
595
					// Do not take offline nodes into account, unless told to do
					// so...
					if (onlyOnline && !currentView
							.containsNode(mac.getHost().getId())) {
Christoph Storm's avatar
Christoph Storm committed
596
597
						continue;
					}
598
					// Rely on underlay for neighbors
599
600
					List<MacAddress> neighbors = topoView
							.getNeighbors(mac.getMacAddress());
601
602
					for (MacAddress neighborMac : neighbors) {
						// create, but do NOT add the node object
603
604
						INode neighbor = currentView.createNode(
								topoView.getMac(neighborMac).getHost().getId());
605
						// only online nodes (already in graph)
Björn Richerzhagen's avatar
Björn Richerzhagen committed
606
607
						if (!onlyOnline
								|| currentView.containsNode(neighbor.getId())) {
608
609
610
611
							IEdge edge = currentView.createEdge(
									mac.getHost().getId(),
									topoView.getMac(neighborMac).getHost()
											.getId());
Björn Richerzhagen's avatar
Björn Richerzhagen committed
612
							currentView.addElement(edge);
613
614
							edge.setProperty(SiSTypes.PHY_DISTANCE,
									topoView.getDistance(mac.getMacAddress(),
Björn Richerzhagen's avatar
Björn Richerzhagen committed
615
											neighborMac));
616
617
618
619
620
621
622
623
						}
					}
				}
			}

			isInvalid = false;
		}

Björn Richerzhagen's avatar
Björn Richerzhagen committed
624
		public INode getOwnNode(SimHost ownHost) {
625
626
			MacLayer mac = ownHost.getLinkLayer().getMac(phy);
			if (!onlyOnline || mac.isOnline()) {
Björn Richerzhagen's avatar
Björn Richerzhagen committed
627
				return currentView.createNode(ownHost.getId());
628
629
630
631
			}
			return null;
		}

Björn Richerzhagen's avatar
Björn Richerzhagen committed
632
		public Set<IEdge> getNeighbors(SimHost ownHost) {
633
			recalculateLocalView();
Björn Richerzhagen's avatar
Björn Richerzhagen committed
634
635
			INode ownNode = getOwnNode(ownHost);
			return currentView.getOutgoingEdges(ownNode.getId());
636
637
638
639
640
641
642
643
644
645
646
647
648
649
		}

		/**
		 * This is the global view, therefore we do not distinguish between
		 * hosts.
		 * 
		 * @return
		 */
		public Graph getLocalView() {
			recalculateLocalView();
			return currentView;
		}

		@Override
650
		public void onLocationChanged(Host host, Location location) {
651
			this.isInvalid = true;
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
		}

		@Override
		public void wentOnline(Host host, NetInterface netInterface) {
			assert netInterface.getName() == medium;
			this.isInvalid = true;
		}

		@Override
		public void wentOffline(Host host, NetInterface netInterface) {
			assert netInterface.getName() == medium;
			this.isInvalid = true;
		}

	}

668
669
670
671
672
	@Override
	public String toString() {
		return "TopoComp: " + getHost().getId() + " at " + position.toString();
	}

673
}