AbstractMovementModel.java 7.65 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
/*
 * Copyright (c) 2005-2011 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;

import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;

29
import de.tud.kom.p2psim.api.topology.Topology;
30
import de.tud.kom.p2psim.api.topology.TopologyComponent;
31
32
33
import de.tud.kom.p2psim.api.topology.movement.MovementListener;
import de.tud.kom.p2psim.api.topology.movement.MovementModel;
import de.tud.kom.p2psim.api.topology.movement.MovementSupported;
34
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
35
import de.tud.kom.p2psim.api.topology.placement.PlacementModel;
36
import de.tud.kom.p2psim.impl.topology.PositionVector;
37
import de.tud.kom.p2psim.impl.topology.TopologyFactory;
38
import de.tudarmstadt.maki.simonstrator.api.Binder;
39
40
41
42
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Randoms;
import de.tudarmstadt.maki.simonstrator.api.Time;
43
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location;
44
45
46
47
48
49
50
51
52
53
54
55

/**
 * Unified movement Models. Can be used inside an Application (virtual Position)
 * or Device (physical Position) or anything else that implements
 * MovementSupported. They support automatic triggering using an Operation or
 * you may trigger them directly from within your Application by calling move()
 * 
 * @author Bjoern Richerzhagen
 * @version 1.0, 04/25/2011
 */
public abstract class AbstractMovementModel implements MovementModel {

56
	private Set<SimLocationActuator> components = new LinkedHashSet<SimLocationActuator>();
57
58
59
60
61
62
63

	protected PositionVector worldDimensions;

	private long timeBetweenMoveOperations = -1;

	private Random random = Randoms.getRandom(AbstractMovementModel.class);

64
65
66
67
68
69
70
71
	/**
	 * This default implementation relies on {@link PlacementModel}s to be
	 * configured in the {@link TopologyFactory}
	 */
	@Override
	public void placeComponent(SimLocationActuator actuator) {
		// not supported
	}
72
73
74
75
76
77
78
	
	@Override
	public void changeTargetLocation(SimLocationActuator actuator,
			double longitude, double latitude) throws UnsupportedOperationException {
		// not supported by default. Extend this method, if needed.
		throw new UnsupportedOperationException();
	}
79

80
81
82
83
84
85
86
87
88
89
90
	/**
	 * Gets called periodically (after timeBetweenMoveOperations) or by an
	 * application and should be used to recalculate positions
	 */
	public abstract void move();

	/**
	 * Get all participating Components
	 * 
	 * @return
	 */
91
	protected Set<SimLocationActuator> getComponents() {
92
93
94
95
96
97
98
99
100
101
102
		return components;
	}

	/**
	 * Add a component to this movement-Model (used to keep global information
	 * about all participants in a movement model). Each Component acts as a
	 * callback upon movement of this component
	 * 
	 * @param component
	 */
	@Override
103
	public void addComponent(SimLocationActuator component) {
104
		if (worldDimensions == null) {
105
106
			worldDimensions = Binder.getComponentOrNull(Topology.class)
					.getWorldDimensions();
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
		}
		components.add(component);
	}

	/**
	 * Get a valid delta-Vector (does not cross world-boundaries and does not
	 * exceed moveSpeedLimit)
	 * 
	 * @param oldPosition
	 * @param minSpeed
	 * @param maxSpeed
	 * @return
	 */
	protected PositionVector getRandomDeltaWithinSpeed(
			PositionVector oldPosition, double minSpeed, double maxSpeed) {
122
123
124
		return getRandomDelta(oldPosition,
				minSpeed * getTimeBetweenMoveOperations() / Time.SECOND,
				maxSpeed * getTimeBetweenMoveOperations() / Time.SECOND);
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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
170
171
172
	}

	/**
	 * Get a valid delta-Vector, where each abs(vector) must not exceed
	 * maxLength
	 * 
	 * @param oldPosition
	 * @param minLength
	 * @param maxLength
	 * @return
	 */
	protected PositionVector getRandomDelta(PositionVector oldPosition,
			double minLength, double maxLength) {
		assert minLength <= maxLength;
		PositionVector delta = null;

		if (oldPosition.getDimensions() == 2) {
			double angle = random.nextDouble() * 2 * Math.PI;
			double length = random.nextDouble() * (maxLength - minLength)
					+ minLength;
			double vectorX = -length * Math.sin(angle);
			double vectorY = length * Math.cos(angle);
			delta = new PositionVector(vectorX, vectorY);
		} else {
			throw new AssertionError(
					"Rotation for R3 and larger are not yet implemented. Feel free to do so ;)");
		}

		assert delta.getLength() >= minLength && delta.getLength() <= maxLength;

		return delta;
	}

	/**
	 * Returns if this is a valid position within the boundaries
	 * 
	 * @return
	 */
	protected boolean isValidPosition(PositionVector vector) {
		for (int i = 0; i < vector.getDimensions(); i++) {
			if (vector.getEntry(i) > getWorldDimension(i)
					|| vector.getEntry(i) < 0) {
				return false;
			}
		}
		return true;
	}

173
174
175
176
177
178
179
180
	/**
	 * Call this method to finally update the location of the given component.
	 * 
	 * @param actuator
	 * @param newPosition
	 */
	protected void updatePosition(SimLocationActuator actuator,
			PositionVector newPosition) {
181
182
		this.updatePosition(actuator, newPosition.getLongitude(),
				newPosition.getLatitude());
183
	}
184

185
186
187
188
189
190
191
192
193
194
195
	/**
	 * Call this method to finally update the location of the given component.
	 * 
	 * @param actuator
	 * @param newPosition
	 */
	protected void updatePosition(SimLocationActuator actuator,
			double longitude, double latitude) {
		actuator.updateCurrentLocation(longitude, latitude);
	}

196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
	/**
	 * Returns a random int between from and to, including both interval ends!
	 * 
	 * @param from
	 * @param to
	 * @return
	 */
	protected int getRandomInt(int from, int to) {
		int intervalSize = Math.abs(to - from);
		return (random.nextInt(intervalSize + 1) + from);
	}

	protected double getRandomDouble(double from, double to) {
		double intervalSize = Math.abs(to - from);
		return (random.nextDouble() * intervalSize + from);
	}

	/**
	 * Move models can periodically calculate new positions and notify
	 * listeners, if a position changed. Here you can specify the interval
	 * between these notifications/calculations. If this is set to zero, there
	 * is no periodical execution, which may be useful if you want to call
	 * move() from within an application.
	 * 
	 * @param timeBetweenMoveOperations
	 */
	public void setTimeBetweenMoveOperations(long timeBetweenMoveOperations) {
		if (timeBetweenMoveOperations > 0) {
			this.timeBetweenMoveOperations = timeBetweenMoveOperations;
			assert timeBetweenMoveOperations > 0;
			reschedule();
		}
	}

	protected void reschedule() {
		Event.scheduleWithDelay(timeBetweenMoveOperations, new PeriodicMove(),
				null, 0);
	}

	/**
	 * The time in Simulation units between move operations
	 * 
	 * @return
	 */
	public long getTimeBetweenMoveOperations() {
		return timeBetweenMoveOperations;
	}

	public double getWorldDimension(int dim) {
		return worldDimensions.getEntry(dim);
	}

	/**
	 * Triggers the periodic move
	 * 
	 * @author Bjoern Richerzhagen
	 * @version 1.0, 20.03.2012
	 */
	protected class PeriodicMove implements EventHandler {

		@Override
		public void eventOccurred(Object content, int type) {
			move();
			reschedule();
		}

	}

}