ModularMovementModel.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
23
24
25
26
27
28
29
30
31
/*
 * 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.movement.modularosm;

import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.Vector;

import de.tud.kom.p2psim.api.scenario.ConfigurationException;
32
import de.tud.kom.p2psim.api.topology.Topology;
33
import de.tud.kom.p2psim.api.topology.movement.MovementModel;
34
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
35
import de.tud.kom.p2psim.api.topology.movement.local.LocalMovementStrategy;
36
import de.tud.kom.p2psim.api.topology.placement.PlacementModel;
37
import de.tud.kom.p2psim.impl.simengine.Simulator;
38
import de.tud.kom.p2psim.impl.topology.DefaultTopology;
39
import de.tud.kom.p2psim.impl.topology.TopologyFactory;
40
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.AttractionPointViz;
41
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.IAttractionProvider;
42
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.MobileAttractionPoint;
43
import de.tud.kom.p2psim.impl.topology.movement.modularosm.mapvisualization.IMapVisualization;
Julian Zobel's avatar
wip    
Julian Zobel committed
44
45
import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.IAttractionAssigmentStrategy;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.IAttractionAssigmentStrategy.AttractionAssignmentListener;
46
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
47
48
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tud.kom.p2psim.impl.util.Either;
49
import de.tudarmstadt.maki.simonstrator.api.Binder;
50
51
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
52
import de.tudarmstadt.maki.simonstrator.api.Monitor;
53
import de.tudarmstadt.maki.simonstrator.api.Randoms;
54
import de.tudarmstadt.maki.simonstrator.api.Time;
55
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
56
57
58

/**
 * Modular Movement Model uses different models/strategies to create a movement
59
 * model. In this implementation, it has 3 different models/strategies.
60
 * <p>
61
62
 * M0: AttractionGenerator -> Generates the {@link IAttractionPoint}s and place
 * them on the map. The {@link IAttractionPoint}s can't be moved, because they
63
 * are static POIs from real-world data!
64
 * <p>
65
66
 * M1: A general {@link MovementModel} is not used, because we use static
 * attraction points.
67
 * <p>
Julian Zobel's avatar
wip    
Julian Zobel committed
68
 * M2: The {@link IAttractionAssigmentStrategy}! It takes the Hosts, which should be moved
69
 * around, but calculates only the assignment to the {@link IAttractionPoint}s.
70
71
72
73
74
75
76
77
78
 * It doesn't move the Hosts! It will be only assignment a new AttractionPoint!
 * 
 * <p>
 * M3: The {@link LocalMovementStrategy} is responsible for the movement of the
 * Hosts. It moves the hosts to the assigned AttractionPoint, and if the
 * AttractionPoint has moved, then will be followed. The
 * {@link LocalMovementStrategy} will be called from the
 * {@link ModularMovementModel} to do a Movement!
 * <p>
79
 * This class contains all three components and manage the data exchange.
80
81
82
83
84
85
 * Additionally it contains an periodic operation, which handle the movement of
 * all hosts. This mean, that it will be call the {@link LocalMovementStrategy}
 * with the destination. Please take care, that the handling of the movement of
 * the AttractionPoints will be handled by the movement model in M1! <br>
 * Further it contains an offset for every Host, which will be added to the
 * destination point (AttractionPoint), so that not all hosts, which are
86
 * assigned to one {@link IAttractionPoint}, lies on the same point.<br>
87
 * 
Clemens Krug's avatar
Clemens Krug committed
88
89
90
91
92
93
 * CHANGELOG
 *
 * - 04.01.2017 Clemens Krug: Added the possibility to configure the model
 * visualisation via XML. If not specified, the visualisation will use the
 * {@link ModularMovementModelViz}, just as before. Thus there shouldn't be any problems
 * with older code.
94
 * 
95
 * @author Martin Hellwig, Christoph Muenker
96
 * @version 1.0, 07.07.2015
97
 */
98
public class ModularMovementModel implements MovementModel, EventHandler, AttractionAssignmentListener {
99

100
	protected final int EVENT_MOVE = 1;
101

102
	protected final int EVENT_INIT = 2;
103

104
105
	protected PositionVector worldDimensions;

Julian Zobel's avatar
wip    
Julian Zobel committed
106
	protected IAttractionAssigmentStrategy attractionAssigment;
107

108
	protected IAttractionProvider attractionProvider;
109
110

	protected LocalMovementStrategy localMovementStrategy;
111

112
	protected IMapVisualization mapVisualization;
113

114
	protected ModularMovementModelViz modelVisualisation;
115
116
	
	protected AttractionPointViz attractionPointViz;
Clemens Krug's avatar
Clemens Krug committed
117

118
	protected Set<SimLocationActuator> moveableHosts = new LinkedHashSet<SimLocationActuator>();
119

120
	protected Map<SimLocationActuator, PositionVector> currentTargets = new LinkedHashMap<>();
121

122
	protected Map<SimLocationActuator, RouteSensorComponent> routeSensorComponents = new LinkedHashMap<>();
123

124
	protected boolean initialized = false;
125

126
	protected long timeBetweenMoveOperation = Simulator.SECOND_UNIT;
127

128
	protected Random rand = Randoms.getRandom(ModularMovementModel.class);
129

130
131
	protected boolean placeNodesAtAP = false;
	
132
	public ModularMovementModel() {
133
134
		this.worldDimensions = Binder.getComponentOrNull(Topology.class)
				.getWorldDimensions();
135
		
136
137
138
		// scheduling initalization!
		Event.scheduleImmediately(this, null, EVENT_INIT);
	}
139
140
141
142
		
	public void setPlaceNodes(boolean placeNodes) {
		this.placeNodesAtAP = placeNodes;
	}
143
144
145
146
147
148

	/**
	 * This Method will be not called from the Components. So we call this
	 * manually!
	 */
	public void initialize() {
149

150
		if (!initialized) {
Julian Zobel's avatar
wip    
Julian Zobel committed
151
						
152
153
154
			if (modelVisualisation == null) {
				modelVisualisation = new ModularMovementModelViz(this);
			}
Clemens Krug's avatar
Clemens Krug committed
155
			VisualizationInjector.injectComponent(modelVisualisation);
156
157
158
			if (mapVisualization != null) {
				VisualizationInjector.injectComponent(mapVisualization);
			}
159
160
161
162
			if (attractionPointViz != null) {
				System.out.println("insert AP viz");
				VisualizationInjector.injectComponent(attractionPointViz);
			}
163
164
165
166

			checkConfiguration();

			// setWayPointModel
167
168
169
170
			localMovementStrategy.setObstacleModel(Binder
					.getComponentOrNull(Topology.class).getObstacleModel());
			localMovementStrategy.setWaypointModel(Binder
					.getComponentOrNull(Topology.class).getWaypointModel());
171

172
173
174
175
176
			/*
			 * Scale depending on calculation interval, if interval != 1 Second.
			 */
			localMovementStrategy
					.setScaleFactor(timeBetweenMoveOperation / (double) Time.SECOND);
177
			
Julian Zobel's avatar
wip    
Julian Zobel committed
178
			attractionAssigment.addAttractionAssignmentListener(this);
179
			attractionAssigment.setAttractionProvider(attractionProvider);
180
181
182

			// This adds the mobile hosts (smartphones/users) to the transition
			// strategy
183
			for (SimLocationActuator ms : moveableHosts) {
Julian Zobel's avatar
wip    
Julian Zobel committed
184
				attractionAssigment.addComponent(ms);
185
186
				
				if(placeNodesAtAP) {
187
188
189
					IAttractionPoint assignment = attractionAssigment.getAssignment(ms);										
					ms.updateCurrentLocation(this.addOffset(new PositionVector(assignment), 
							(assignment.hasRadius() ? Math.max(assignment.getRadius(), 25.0) : 25.0)));	
190
191
					attractionAssigment.updateTargetAttractionPoint(ms, assignment);
				}				
192
193
194
195
196
197
198
199
			}

			setTimeBetweenMoveOperations(timeBetweenMoveOperation);

			// initial move
			move();

			initialized = true;
200
201
202
			
			// Inform analyzer of resolved movement
			if(Monitor.hasAnalyzer(IAttractionBasedMovementAnalyzer.class)) {			
203
				Monitor.getOrNull(IAttractionBasedMovementAnalyzer.class).onAllNodeInitializationCompleted(moveableHosts);
204
205
			}
			
206
207
		}
	}
208

209
210
211
212
213
214
215
216
	/**
	 * This default implementation relies on {@link PlacementModel}s to be
	 * configured in the {@link TopologyFactory}
	 */
	@Override
	public void placeComponent(SimLocationActuator actuator) {
		// not supported
	}
217

218
	@Override
219
	public void changeTargetLocation(SimLocationActuator actuator, IAttractionPoint ap) {
Julian Zobel's avatar
wip    
Julian Zobel committed
220
		attractionAssigment.updateTargetAttractionPoint(actuator, ap);
221
	}	
222
		
223
	protected void checkConfiguration() {
224
225
226
227
		if (localMovementStrategy == null) {
			throw new ConfigurationException(
					"LocalMovementStrategy is missing in ModularMovementModel!");
		}
Julian Zobel's avatar
wip    
Julian Zobel committed
228
		if (attractionAssigment == null) {
229
230
231
232
233
234
			throw new ConfigurationException(
					"TransitionStrategy is missing in ModularMovementModel!");
		}
	}

	@Override
235
	public void addComponent(SimLocationActuator comp) {
236
		moveableHosts.add(comp);
237
238
239
		if (!routeSensorComponents.containsKey(comp)) {
			routeSensorComponents.put(comp, new RouteSensorComponent(comp));
		}
240
	}
241
	
242
243
	@Override
	public void updatedAttractionAssignment(SimLocationActuator component,
244
			IAttractionPoint newAssignment) {
Julian Zobel's avatar
wip    
Julian Zobel committed
245
				
246
247
248
249
250
		/*
		 * Use this method to calculate the offset and target location for a
		 * host.
		 */
		PositionVector attractionCenter = (PositionVector) newAssignment;
251
		PositionVector destination = null;
252
253
254
		/*
		 * Even if an AP does not have a radius, we slightly offset
		 */
Julian Zobel's avatar
wip    
Julian Zobel committed
255
		double apRadius = (newAssignment.hasRadius() ? Math.max(newAssignment.getRadius(), 25.0) : 25.0);
256

257
258
		int tries = 0;
		do {
259
			destination = addOffset(attractionCenter, apRadius);		
260
			// Check constraints
Julian Zobel's avatar
wip    
Julian Zobel committed
261
			if (!checkBoundaries(destination)) {
262
263
264
265
266
267
268
				destination = null;
				if (tries > 100) {
					throw new AssertionError("Unable to find a valid target destination within <100 tries.");
				}
			}
			tries++;
		} while (destination == null);
Julian Zobel's avatar
wip    
Julian Zobel committed
269
	
270
		currentTargets.put(component, destination);
271
272
273
	}

	protected void move() {
274
		for (SimLocationActuator component : moveableHosts) {
275
276
			assert currentTargets.containsKey(component);
			doLocalMovement(component, currentTargets.get(component));
277
		}
278
		
279
280
281
282
283
284
		for (IAttractionPoint aps : getAttractionPoints()) {
			if(aps instanceof MobileAttractionPoint) {
				((MobileAttractionPoint) aps).move();
			}
		}
		
285
286
287
		Event.scheduleWithDelay(timeBetweenMoveOperation, this, null,
				EVENT_MOVE);
	}
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
		
	/**
	 * Add a random gaussian offset to the given position. The offset will be within the given radius 
	 * around the given position with a standard deviation of a third of the radius. Thus, the returned
	 * positions are more clustered around the position center.  
	 * 
	 * @param position
	 * @param radius
	 * @return
	 */
	public PositionVector addOffset(PositionVector position, double radius) {
		
		/*
		 * This function will use a gaussian offset from the center. Most values are therefore more clustered around the position,
		 * only a few on the outskirts of the circle. 
		 * 
		 * For a uniform distribution, see for example https://stackoverflow.com/a/50746409
		 */
		
307
		PositionVector offsetPosition = new PositionVector(position);
308
309
310
311
312
		
		double rad = Math.min(rand.nextGaussian() * radius / 3, radius);
		double angle = rand.nextDouble() * 2 * Math.PI;
		
		PositionVector offset = new PositionVector(rad * Math.cos(angle), rad * Math.sin(angle));		
313
		offsetPosition.add(offset);
314
		
315
316
		return offsetPosition;
	}
317
318
319
320
321
322
323
324
325
326

	/**
	 * 
	 * Ask the local movement strategy for the next position. It may return the
	 * next position or a boolean with true to notify the movement model that it
	 * can't get any closer to the current way point.
	 * 
	 * @param ms
	 * @param destination
	 */
327
328
329
	protected void doLocalMovement(SimLocationActuator ms, PositionVector destination) {
		Either<PositionVector, Boolean> either = localMovementStrategy.nextPosition(ms, destination);
		
330
		if (either.hasLeft()) {
331
			ms.updateCurrentLocation(either.getLeft());
Julian Zobel's avatar
wip    
Julian Zobel committed
332
333
334
			if(!checkBoundaries(ms.getRealPosition())) {
				System.err.println("Modular Movement Model: Host moved outside of simulated area!");
			}				
335
336
		} 
		else {
Julian Zobel's avatar
wip    
Julian Zobel committed
337
			attractionAssigment.reachedAttractionPoint(ms, ms.getCurrentTargetAttractionPoint());
338
339
340
		}
	}

341
342
343
344
345
346
347
348
349
350
	/* 
	 * =====================================================================================================
	 * === HELPER FUNCTIONS
	 * =====================================================================================================
	 */
	/**
	 * Notifies the user if a hosts position lies outside of the specified world size.
	 * Enable asserts to get the notification
	 * 
	 */
Julian Zobel's avatar
wip    
Julian Zobel committed
351
	public boolean checkBoundaries(PositionVector position) {		
352
		return DefaultTopology.isWithinWorldBoundaries(position);
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
	}

	@Override
	public void eventOccurred(Object content, int type) {
		if (type == EVENT_INIT) {
			initialize();
		} else if (type == EVENT_MOVE) {
			move();
		}
	}
	
	/* 
	 * =====================================================================================================
	 * === GETTER AND SETTER FUNCTIONS
	 * =====================================================================================================
	 */
	public Set<SimLocationActuator> getAllLocationActuators() {
		return moveableHosts;
	}

	@Override
	public void setTimeBetweenMoveOperations(long time) {
		if (time > 0) {
			this.timeBetweenMoveOperation = time;
		} else {
			throw new ConfigurationException(
					"time is negative for the Move Operations");
		}
	}

	
384
385
	public void setIAttractionProvider(IAttractionProvider attractionProvider) {
		if (attractionProvider == null) {
386
			throw new ConfigurationException(
387
					"AttractionProvider is missing in ModularMovementModel!");
388
		}
389
		this.attractionProvider = attractionProvider;
390
391
	}

392
393
394
395
396
	public void setLocalMovementStrategy(LocalMovementStrategy localMovementStrategy) {
		if (localMovementStrategy == null) {
			throw new ConfigurationException(
					"LocalMovementStrategy is missing in ModularMovementModel!");
		}
397
398
399
		this.localMovementStrategy = localMovementStrategy;
	}

Julian Zobel's avatar
wip    
Julian Zobel committed
400
	public void setITransitionStrategy(IAttractionAssigmentStrategy transition) {
401
402
403
404
		if (transition == null) {
			throw new ConfigurationException(
					"TransitionStrategy is missing in ModularMovementModel!");
		}
Julian Zobel's avatar
wip    
Julian Zobel committed
405
		this.attractionAssigment = transition;
406
	}
407

408
409
410
	public void setIMapVisualization(IMapVisualization mapVisualization) {
		this.mapVisualization = mapVisualization;
	}
411

Clemens Krug's avatar
Clemens Krug committed
412
413
414
415
416
417
	public void setModelVisualisation(ModularMovementModelViz modelVis)
	{
		modelVisualisation = modelVis;
        modelVisualisation.setMovementModel(this);
	}

418
	@Override
419
	public IAttractionPoint getTargetLocation(SimLocationActuator actuator) {
Julian Zobel's avatar
wip    
Julian Zobel committed
420
		return attractionAssigment.getAssignment(actuator);
421
	}
422
	
423
424
425
426
427
	/**
	 * Only for visualization!
	 * 
	 * @return
	 */
428
	public List<IAttractionPoint> getAttractionPoints() {
429
		return new Vector<IAttractionPoint>(attractionProvider.getAttractionPoints());
430
	}
431
432
433
434
	
	public void setAttractionPointViz(AttractionPointViz viz) {
		this.attractionPointViz = viz;
	}
435
}