ModularMovementModel.java 14.4 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.IAttractionGenerator;
42
import de.tud.kom.p2psim.impl.topology.movement.modularosm.mapvisualization.IMapVisualization;
Julian Zobel's avatar
wip    
Julian Zobel committed
43
44
import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.IAttractionAssigmentStrategy;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.IAttractionAssigmentStrategy.AttractionAssignmentListener;
45
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
46
47
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tud.kom.p2psim.impl.util.Either;
48
import de.tudarmstadt.maki.simonstrator.api.Binder;
49
50
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
51
import de.tudarmstadt.maki.simonstrator.api.Monitor;
52
import de.tudarmstadt.maki.simonstrator.api.Randoms;
53
import de.tudarmstadt.maki.simonstrator.api.Time;
54
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
55
56
57

/**
 * Modular Movement Model uses different models/strategies to create a movement
58
 * model. In this implementation, it has 3 different models/strategies.
59
 * <p>
60
61
 * M0: AttractionGenerator -> Generates the {@link IAttractionPoint}s and place
 * them on the map. The {@link IAttractionPoint}s can't be moved, because they
62
 * are static POIs from real-world data!
63
 * <p>
64
65
 * M1: A general {@link MovementModel} is not used, because we use static
 * attraction points.
66
 * <p>
Julian Zobel's avatar
wip    
Julian Zobel committed
67
 * M2: The {@link IAttractionAssigmentStrategy}! It takes the Hosts, which should be moved
68
 * around, but calculates only the assignment to the {@link IAttractionPoint}s.
69
70
71
72
73
74
75
76
77
 * 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>
78
 * This class contains all three components and manage the data exchange.
79
80
81
82
83
84
 * 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
85
 * assigned to one {@link IAttractionPoint}, lies on the same point.<br>
86
 * 
Clemens Krug's avatar
Clemens Krug committed
87
88
89
90
91
92
 * 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.
93
 * 
94
 * @author Martin Hellwig, Christoph Muenker
95
 * @version 1.0, 07.07.2015
96
 */
97
public class ModularMovementModel implements MovementModel, EventHandler, AttractionAssignmentListener {
98

99
	protected final int EVENT_MOVE = 1;
100

101
	protected final int EVENT_INIT = 2;
102

103
104
	protected PositionVector worldDimensions;

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

	protected IAttractionGenerator attractionGenerator;

	protected LocalMovementStrategy localMovementStrategy;
110

111
	protected IMapVisualization mapVisualization;
112

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

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

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

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

123
	protected boolean initialized = false;
124

125
	protected long timeBetweenMoveOperation = Simulator.SECOND_UNIT;
126

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

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

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

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

			checkConfiguration();

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

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

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

			setTimeBetweenMoveOperations(timeBetweenMoveOperation);

			// initial move
			move();

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

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

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

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

255
256
		int tries = 0;
		do {
257
			destination = addOffset(attractionCenter, apRadius);		
258
			// Check constraints
Julian Zobel's avatar
wip    
Julian Zobel committed
259
			if (!checkBoundaries(destination)) {
260
261
262
263
264
265
266
				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
267
	
268
		currentTargets.put(component, destination);
269
270
271
	}

	protected void move() {
272
		for (SimLocationActuator component : moveableHosts) {
273
274
			assert currentTargets.containsKey(component);
			doLocalMovement(component, currentTargets.get(component));
275
		}
276
		
277
278
279
		Event.scheduleWithDelay(timeBetweenMoveOperation, this, null,
				EVENT_MOVE);
	}
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
		
	/**
	 * 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
		 */
		
299
		PositionVector offsetPosition = new PositionVector(position);
300
301
302
303
304
		
		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));		
305
		offsetPosition.add(offset);
306
		
307
308
		return offsetPosition;
	}
309
310
311
312
313
314
315
316
317
318

	/**
	 * 
	 * 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
	 */
319
320
321
	protected void doLocalMovement(SimLocationActuator ms, PositionVector destination) {
		Either<PositionVector, Boolean> either = localMovementStrategy.nextPosition(ms, destination);
		
322
		if (either.hasLeft()) {
323
			ms.updateCurrentLocation(either.getLeft());
Julian Zobel's avatar
wip    
Julian Zobel committed
324
325
326
			if(!checkBoundaries(ms.getRealPosition())) {
				System.err.println("Modular Movement Model: Host moved outside of simulated area!");
			}				
327
328
		} 
		else {
Julian Zobel's avatar
wip    
Julian Zobel committed
329
			attractionAssigment.reachedAttractionPoint(ms, ms.getCurrentTargetAttractionPoint());
330
331
332
		}
	}

333
334
335
336
337
338
339
340
341
342
	/* 
	 * =====================================================================================================
	 * === 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
343
	public boolean checkBoundaries(PositionVector position) {		
344
		return DefaultTopology.isWithinWorldBoundaries(position);
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
	}

	@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");
		}
	}

	
	public void setIAttractionGenerator(IAttractionGenerator attractionGenerator) {
		if (attractionGenerator == null) {
			throw new ConfigurationException(
					"AttractionGenerator is missing in ModularMovementModel!");
		}
381
382
383
		this.attractionGenerator = attractionGenerator;
	}

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

Julian Zobel's avatar
wip    
Julian Zobel committed
392
	public void setITransitionStrategy(IAttractionAssigmentStrategy transition) {
393
394
395
396
		if (transition == null) {
			throw new ConfigurationException(
					"TransitionStrategy is missing in ModularMovementModel!");
		}
Julian Zobel's avatar
wip    
Julian Zobel committed
397
		this.attractionAssigment = transition;
398
	}
399

400
401
402
	public void setIMapVisualization(IMapVisualization mapVisualization) {
		this.mapVisualization = mapVisualization;
	}
403

Clemens Krug's avatar
Clemens Krug committed
404
405
406
407
408
409
	public void setModelVisualisation(ModularMovementModelViz modelVis)
	{
		modelVisualisation = modelVis;
        modelVisualisation.setMovementModel(this);
	}

410
	@Override
411
	public IAttractionPoint getTargetLocation(SimLocationActuator actuator) {
Julian Zobel's avatar
wip    
Julian Zobel committed
412
		return attractionAssigment.getAssignment(actuator);
413
	}
414
	
415
416
417
418
419
	/**
	 * Only for visualization!
	 * 
	 * @return
	 */
420
421
	public List<IAttractionPoint> getAttractionPoints() {
		return new Vector<IAttractionPoint>(IAttractionGenerator.attractionPoints);
422
	}
423
424
425
426
	
	public void setAttractionPointViz(AttractionPointViz viz) {
		this.attractionPointViz = viz;
	}
427
}