ModularMovementModel.java 14.5 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.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
	protected IAttractionProvider attractionProvider;
108
109

	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
			attractionAssigment.setAttractionProvider(attractionProvider);
179
180
181

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

			setTimeBetweenMoveOperations(timeBetweenMoveOperation);

			// initial move
			move();

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

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

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

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

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

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

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

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

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

	
377
378
	public void setIAttractionProvider(IAttractionProvider attractionProvider) {
		if (attractionProvider == null) {
379
			throw new ConfigurationException(
380
					"AttractionProvider is missing in ModularMovementModel!");
381
		}
382
		this.attractionProvider = attractionProvider;
383
384
	}

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

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

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

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

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