/*
* 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 .
*
*/
package de.tud.kom.p2psim.impl.topology.movement;
import java.nio.channels.UnsupportedAddressTypeException;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import de.tud.kom.p2psim.api.topology.Topology;
import de.tud.kom.p2psim.api.topology.TopologyComponent;
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;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.api.topology.placement.PlacementModel;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.topology.TopologyFactory;
import de.tudarmstadt.maki.simonstrator.api.Binder;
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;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location;
/**
* 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 {
private Set components = new LinkedHashSet();
protected PositionVector worldDimensions;
private long timeBetweenMoveOperations = -1;
private Random random = Randoms.getRandom(AbstractMovementModel.class);
/**
* This default implementation relies on {@link PlacementModel}s to be
* configured in the {@link TopologyFactory}
*/
@Override
public void placeComponent(SimLocationActuator actuator) {
// not supported
}
/**
* 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
*/
protected Set getComponents() {
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
public void addComponent(SimLocationActuator component) {
if (worldDimensions == null) {
worldDimensions = Binder.getComponentOrNull(Topology.class)
.getWorldDimensions();
}
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) {
return getRandomDelta(oldPosition,
minSpeed * getTimeBetweenMoveOperations() / Time.SECOND,
maxSpeed * getTimeBetweenMoveOperations() / Time.SECOND);
}
/**
* 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;
}
/**
* Call this method to finally update the location of the given component.
*
* @param actuator
* @param newPosition
*/
protected void updatePosition(SimLocationActuator actuator,
PositionVector newPosition) {
actuator.updateCurrentLocation(newPosition);
}
/**
* 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();
}
}
}