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

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

97
	protected final int EVENT_MOVE = 1;
98

99
	protected final int EVENT_INIT = 2;
100

101
102
	protected PositionVector worldDimensions;

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

	protected IAttractionGenerator attractionGenerator;

	protected LocalMovementStrategy localMovementStrategy;
108

109
	protected IMapVisualization mapVisualization;
110

111
	protected ModularMovementModelViz modelVisualisation;
Clemens Krug's avatar
Clemens Krug committed
112

113
	protected Set<SimLocationActuator> moveableHosts = new LinkedHashSet<SimLocationActuator>();
114

115
	protected Map<SimLocationActuator, PositionVector> currentTargets = new LinkedHashMap<>();
116

117
	protected Map<SimLocationActuator, RouteSensorComponent> routeSensorComponents = new LinkedHashMap<>();
118

119
	protected boolean initialized = false;
120

121
	protected long timeBetweenMoveOperation = Simulator.SECOND_UNIT;
122

123
	protected Random rand = Randoms.getRandom(ModularMovementModel.class);
124
125

	public ModularMovementModel() {
126
127
		this.worldDimensions = Binder.getComponentOrNull(Topology.class)
				.getWorldDimensions();
128
		
129
130
131
132
133
134
135
136
137
		// scheduling initalization!
		Event.scheduleImmediately(this, null, EVENT_INIT);
	}

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

139
		if (!initialized) {
Julian Zobel's avatar
wip    
Julian Zobel committed
140
						
141
142
143
			if (modelVisualisation == null) {
				modelVisualisation = new ModularMovementModelViz(this);
			}
Clemens Krug's avatar
Clemens Krug committed
144
			VisualizationInjector.injectComponent(modelVisualisation);
145
146
147
			if (mapVisualization != null) {
				VisualizationInjector.injectComponent(mapVisualization);
			}
148
149
150
151

			checkConfiguration();

			// setWayPointModel
152
153
154
155
			localMovementStrategy.setObstacleModel(Binder
					.getComponentOrNull(Topology.class).getObstacleModel());
			localMovementStrategy.setWaypointModel(Binder
					.getComponentOrNull(Topology.class).getWaypointModel());
156

157
158
159
160
161
			/*
			 * Scale depending on calculation interval, if interval != 1 Second.
			 */
			localMovementStrategy
					.setScaleFactor(timeBetweenMoveOperation / (double) Time.SECOND);
162
			
Julian Zobel's avatar
wip    
Julian Zobel committed
163
			attractionAssigment.addAttractionAssignmentListener(this);
164
165
166

			// This adds the mobile hosts (smartphones/users) to the transition
			// strategy
167
			for (SimLocationActuator ms : moveableHosts) {
Julian Zobel's avatar
wip    
Julian Zobel committed
168
				attractionAssigment.addComponent(ms);
169
170
171
172
173
174
175
176
			}

			setTimeBetweenMoveOperations(timeBetweenMoveOperation);

			// initial move
			move();

			initialized = true;
177
178
179
180
181
182
			
			// Inform analyzer of resolved movement
			if(Monitor.hasAnalyzer(IAttractionBasedMovementAnalyzer.class)) {			
				Monitor.getOrNull(IAttractionBasedMovementAnalyzer.class).onAllNodeMovementCompleted(moveableHosts);
			}
			
183
184
		}
	}
185

186
187
188
189
190
191
192
193
	/**
	 * This default implementation relies on {@link PlacementModel}s to be
	 * configured in the {@link TopologyFactory}
	 */
	@Override
	public void placeComponent(SimLocationActuator actuator) {
		// not supported
	}
194

195
	@Override
196
	public void changeTargetLocation(SimLocationActuator actuator, IAttractionPoint ap) {
Julian Zobel's avatar
wip    
Julian Zobel committed
197
		attractionAssigment.updateTargetAttractionPoint(actuator, ap);
198
	}	
199
		
200
	protected void checkConfiguration() {
201
202
203
204
		if (localMovementStrategy == null) {
			throw new ConfigurationException(
					"LocalMovementStrategy is missing in ModularMovementModel!");
		}
Julian Zobel's avatar
wip    
Julian Zobel committed
205
		if (attractionAssigment == null) {
206
207
208
209
210
211
212
213
214
215
			throw new ConfigurationException(
					"TransitionStrategy is missing in ModularMovementModel!");
		}
		if (attractionGenerator == null) {
			throw new ConfigurationException(
					"AttractionGenerator is missing in ModularMovementModel!");
		}
	}

	@Override
216
	public void addComponent(SimLocationActuator comp) {
217
		moveableHosts.add(comp);
218
219
220
		if (!routeSensorComponents.containsKey(comp)) {
			routeSensorComponents.put(comp, new RouteSensorComponent(comp));
		}
221
	}
222
	
223
224
	@Override
	public void updatedAttractionAssignment(SimLocationActuator component,
225
			IAttractionPoint newAssignment) {
Julian Zobel's avatar
wip    
Julian Zobel committed
226
				
227
228
229
230
231
		/*
		 * Use this method to calculate the offset and target location for a
		 * host.
		 */
		PositionVector attractionCenter = (PositionVector) newAssignment;
232
		PositionVector destination = null;
233
234
235
		/*
		 * Even if an AP does not have a radius, we slightly offset
		 */
Julian Zobel's avatar
wip    
Julian Zobel committed
236
		double apRadius = (newAssignment.hasRadius() ? Math.max(newAssignment.getRadius(), 25.0) : 25.0);
237

238
239
		int tries = 0;
		do {
240
			destination = addGaussianOffsetToPosition(attractionCenter, apRadius / 3);		
241
			// Check constraints
Julian Zobel's avatar
wip    
Julian Zobel committed
242
			if (!checkBoundaries(destination)) {
243
244
245
246
247
248
249
				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
250
	
251
		currentTargets.put(component, destination);
252
253
254
	}

	protected void move() {
255
		for (SimLocationActuator component : moveableHosts) {
256
257
			assert currentTargets.containsKey(component);
			doLocalMovement(component, currentTargets.get(component));
258
		}
259
		
260
261
262
		Event.scheduleWithDelay(timeBetweenMoveOperation, this, null,
				EVENT_MOVE);
	}
263
264
265
266
267
268
269
270
	
	public PositionVector addGaussianOffsetToPosition(PositionVector position, double std) {
		PositionVector offsetPosition = new PositionVector(position);
		// Gaussian with std = 1 --> >99% of nodes
		PositionVector offset = new PositionVector(rand.nextGaussian() * std, rand.nextGaussian() * std);
		offsetPosition.add(offset);
		return offsetPosition;
	}
271
272
273
274
275
276
277
278
279
280

	/**
	 * 
	 * 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
	 */
281
282
283
	protected void doLocalMovement(SimLocationActuator ms, PositionVector destination) {
		Either<PositionVector, Boolean> either = localMovementStrategy.nextPosition(ms, destination);
		
284
		if (either.hasLeft()) {
285
			ms.updateCurrentLocation(either.getLeft());
Julian Zobel's avatar
wip    
Julian Zobel committed
286
287
288
			if(!checkBoundaries(ms.getRealPosition())) {
				System.err.println("Modular Movement Model: Host moved outside of simulated area!");
			}				
289
290
		} 
		else {
Julian Zobel's avatar
wip    
Julian Zobel committed
291
			attractionAssigment.reachedAttractionPoint(ms, ms.getCurrentTargetAttractionPoint());
292
293
294
		}
	}

295
296
297
298
299
300
301
302
303
304
	/* 
	 * =====================================================================================================
	 * === 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
305
306
307
308
309
310
311
312
	public boolean checkBoundaries(PositionVector position) {		
		if(position.getX() >= 0.0 
				&& position.getX() <= Binder.getComponentOrNull(Topology.class).getWorldDimensions().getX() 
				&& position.getY() >= 0.0 
				&& position.getY() <= Binder.getComponentOrNull(Topology.class).getWorldDimensions().getY())
			return true;
		else
			return false;
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
	}

	@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!");
		}
349
350
351
		this.attractionGenerator = attractionGenerator;
	}

352
353
354
355
356
	public void setLocalMovementStrategy(LocalMovementStrategy localMovementStrategy) {
		if (localMovementStrategy == null) {
			throw new ConfigurationException(
					"LocalMovementStrategy is missing in ModularMovementModel!");
		}
357
358
359
		this.localMovementStrategy = localMovementStrategy;
	}

Julian Zobel's avatar
wip    
Julian Zobel committed
360
	public void setITransitionStrategy(IAttractionAssigmentStrategy transition) {
361
362
363
364
		if (transition == null) {
			throw new ConfigurationException(
					"TransitionStrategy is missing in ModularMovementModel!");
		}
Julian Zobel's avatar
wip    
Julian Zobel committed
365
		this.attractionAssigment = transition;
366
	}
367

368
369
370
	public void setIMapVisualization(IMapVisualization mapVisualization) {
		this.mapVisualization = mapVisualization;
	}
371

Clemens Krug's avatar
Clemens Krug committed
372
373
374
375
376
377
	public void setModelVisualisation(ModularMovementModelViz modelVis)
	{
		modelVisualisation = modelVis;
        modelVisualisation.setMovementModel(this);
	}

378
	@Override
379
	public IAttractionPoint getTargetLocation(SimLocationActuator actuator) {
Julian Zobel's avatar
wip    
Julian Zobel committed
380
		return attractionAssigment.getAssignment(actuator);
381
	}
382
	
383
384
385
386
387
	/**
	 * Only for visualization!
	 * 
	 * @return
	 */
388
389
	public List<IAttractionPoint> getAttractionPoints() {
		return new Vector<IAttractionPoint>(IAttractionGenerator.attractionPoints);
390
	}
391
}