package de.tud.kom.p2psim.impl.topology.movement.modularosm.transition; import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; import de.tud.kom.p2psim.api.topology.Topology; import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator; import de.tud.kom.p2psim.impl.topology.PositionVector; import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.BasicAttractionPoint; import de.tudarmstadt.maki.simonstrator.api.Binder; import de.tudarmstadt.maki.simonstrator.api.Monitor; import de.tudarmstadt.maki.simonstrator.api.Randoms; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location; /** * This {@link ITransitionStrategy} makes clients move around randomly in a specified area. You can specify the target * area center via the {@link #updateTargetAttractionPoint(SimLocationActuator, AttractionPoint)} method. The client will then start * to roam the area randomly till a new target area is assigned. * * @author Clemens Krug */ public class RandomInAreaTransitionStrategy implements ITransitionStrategy { private final Random random = Randoms .getRandom(RandomInAreaTransitionStrategy.class); private LinkedHashSet aPoints = new LinkedHashSet<>(); /** * These are the target area centers the clients have assigned. */ private Map assignments = new HashMap<>(); /** * These are the current spots inside the target area where the client is currently heading. */ private Map currentTarget = new HashMap<>(); private Map currentSearchRadius = new HashMap<>(); private List listeners = new LinkedList<>(); /** * The radius the target area should have. Should be set via XML config file. */ private int defaultRadius; @Override public AttractionPoint getAssignment(SimLocationActuator comp) { return currentTarget.get(comp); } @Override public void addAttractionAssignmentListener(AttractionAssignmentListener listener) { listeners.add(listener); } @Override public void removeAttractionAssignmentListener(AttractionAssignmentListener listener) { listeners.remove(listener); } @Override public void setAttractionPoints(Collection attractionPoints) { aPoints.addAll(attractionPoints); } @Override public Set getAllAttractionPoints() { return aPoints; } @Override public void addComponent(SimLocationActuator ms) { if(!assignments.containsKey(ms)) { AttractionPoint aPoint = aPoints.iterator().next(); assignments.put(ms, aPoint); currentTarget.put(ms, nextRandomPosition(aPoint, defaultRadius)); currentSearchRadius.put(ms, defaultRadius); } listeners.forEach(listener -> listener.updatedAttractionAssignment(ms, currentTarget.get(ms))); } @Override public void reachedAttractionPoint(SimLocationActuator ms) { currentTarget.put(ms, nextRandomPosition(assignments.get(ms), currentSearchRadius.get(ms))); listeners.forEach(listener -> listener.updatedAttractionAssignment(ms, currentTarget.get(ms))); } @Override public void updateTargetAttractionPoint(SimLocationActuator comp, AttractionPoint attractionPoint) { assignments.put(comp, attractionPoint); currentTarget.put(comp, nextRandomPosition(attractionPoint, currentSearchRadius.get(comp))); listeners.forEach(listener -> listener.updatedAttractionAssignment(comp, attractionPoint)); } public void setSearchRadiusForComponent(SimLocationActuator ms, int radius) { currentSearchRadius.put(ms, radius); } public void setDefaultRadius(int radius) { this.defaultRadius = radius; } /** * Calculates a random point within a given circular area. * @param center The center of the area. * @param radius The radius of the area. * @return A random position within the area. */ private BasicAttractionPoint nextRandomPosition(Location center, int radius) { assert radius > 0 : "An area radius must be specified for the RandomInAreaTransitionStrategy! Did you set the 'DefaultRadius' property for this transition?"; double x = center.getLongitudeOrX(); double y = center.getLatitudeOrY(); double newX = -1; double newY = -1; int tries = 0; while(newX < 0.0 || newX > Binder.getComponentOrNull(Topology.class).getWorldDimensions().getX() || newY < 0.0 || newY > Binder.getComponentOrNull(Topology.class).getWorldDimensions().getY()) { double calcRadius = random.nextDouble() * radius; double calcAngle = random.nextDouble() * 360; newX = x + Math.sin(calcAngle) * calcRadius; newY = y + Math.cos(calcAngle) * calcRadius; tries++; if(tries > 100) throw new AssertionError("Unable to find a valid target destination within <100 tries."); } Monitor.log(this.getClass(), Monitor.Level.DEBUG, "Next random position is " + newX + " / " + newY); return new BasicAttractionPoint("RNDPOS", new PositionVector(newX, newY)); } }