/* * 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 . * */ package de.tud.kom.p2psim.impl.topology.movement.modularosm; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Random; import java.util.Vector; import de.tud.kom.p2psim.api.scenario.ConfigurationException; import de.tud.kom.p2psim.api.topology.Topology; import de.tud.kom.p2psim.api.topology.movement.MovementModel; import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator; import de.tud.kom.p2psim.api.topology.movement.local.LocalMovementStrategy; import de.tud.kom.p2psim.api.topology.placement.PlacementModel; import de.tud.kom.p2psim.impl.simengine.Simulator; import de.tud.kom.p2psim.impl.topology.DefaultTopology; import de.tud.kom.p2psim.impl.topology.TopologyFactory; import de.tud.kom.p2psim.impl.topology.movement.distributions.ISpeedDistributionProvider; import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.AttractionPointViz; import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.IAttractionProvider; import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.MobileAttractionPoint; import de.tud.kom.p2psim.impl.topology.movement.modularosm.mapvisualization.IMapVisualization; import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.IAttractionAssigmentStrategy; import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.IAttractionAssigmentStrategy.AttractionAssignmentListener; import de.tud.kom.p2psim.impl.topology.util.PositionVector; import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector; import de.tud.kom.p2psim.impl.util.Either; 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.Monitor; import de.tudarmstadt.maki.simonstrator.api.Randoms; import de.tudarmstadt.maki.simonstrator.api.Time; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint; /** * Modular Movement Model uses different models/strategies to create a movement * model. In this implementation, it has 3 different models/strategies. *

* M0: AttractionGenerator -> Generates the {@link IAttractionPoint}s and place * them on the map. The {@link IAttractionPoint}s can't be moved, because they * are static POIs from real-world data! *

* M1: A general {@link MovementModel} is not used, because we use static * attraction points. *

* M2: The {@link IAttractionAssigmentStrategy}! It takes the Hosts, which should be moved * around, but calculates only the assignment to the {@link IAttractionPoint}s. * It doesn't move the Hosts! It will be only assignment a new AttractionPoint! * *

* 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! *

* This class contains all three components and manage the data exchange. * 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!
* Further it contains an offset for every Host, which will be added to the * destination point (AttractionPoint), so that not all hosts, which are * assigned to one {@link IAttractionPoint}, lies on the same point.
* * 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. * * @author Martin Hellwig, Christoph Muenker * @version 1.0, 07.07.2015 */ public class ModularMovementModel implements MovementModel, EventHandler, AttractionAssignmentListener { protected final int EVENT_MOVE = 1; protected final int EVENT_INIT = 2; protected PositionVector worldDimensions; protected IAttractionAssigmentStrategy attractionAssigment; protected IAttractionProvider attractionProvider; protected ISpeedDistributionProvider speedProvider; protected LocalMovementStrategy localMovementStrategy; protected IMapVisualization mapVisualization; protected ModularMovementModelViz modelVisualisation; protected AttractionPointViz attractionPointViz; protected LinkedHashSet moveableHosts = new LinkedHashSet(); protected LinkedHashMap currentTargets = new LinkedHashMap<>(); protected LinkedHashMap routeSensorComponents = new LinkedHashMap<>(); protected boolean initialized = false; protected long timeBetweenMoveOperation = Simulator.SECOND_UNIT; protected Random rand = Randoms.getRandom(ModularMovementModel.class); protected boolean placeNodesAtAP = false; public ModularMovementModel() { this.worldDimensions = Binder.getComponentOrNull(Topology.class) .getWorldDimensions(); // scheduling initalization! Event.scheduleImmediately(this, null, EVENT_INIT); } public void setPlaceNodes(boolean placeNodes) { this.placeNodesAtAP = placeNodes; } /** * This Method will be not called from the Components. So we call this * manually! */ public void initialize() { if (!initialized) { if (modelVisualisation == null) { modelVisualisation = new ModularMovementModelViz(this); } VisualizationInjector.injectComponent(modelVisualisation); if (mapVisualization != null) { VisualizationInjector.injectComponent(mapVisualization); } if (attractionPointViz != null) { System.out.println("insert AP viz"); VisualizationInjector.injectComponent(attractionPointViz); } checkConfiguration(); // setWayPointModel localMovementStrategy.setObstacleModel(Binder .getComponentOrNull(Topology.class).getObstacleModel()); localMovementStrategy.setWaypointModel(Binder .getComponentOrNull(Topology.class).getWaypointModel()); /* * Scale depending on calculation interval, if interval != 1 Second. */ localMovementStrategy .setScaleFactor(timeBetweenMoveOperation / (double) Time.SECOND); attractionAssigment.addAttractionAssignmentListener(this); attractionAssigment.setAttractionProvider(attractionProvider); // This adds the mobile hosts (smartphones/users) to the transition // strategy for (SimLocationActuator ms : moveableHosts) { attractionAssigment.addComponent(ms); if(placeNodesAtAP) { IAttractionPoint assignment = attractionAssigment.getAssignment(ms); ms.updateCurrentLocation(this.addOffset(new PositionVector(assignment), (assignment.hasRadius() ? Math.max(assignment.getRadius(), 25.0) : 25.0))); attractionAssigment.updateTargetAttractionPoint(ms, assignment); } } setTimeBetweenMoveOperations(timeBetweenMoveOperation); // initial move move(); initialized = true; // Inform analyzer of resolved movement if(Monitor.hasAnalyzer(IAttractionBasedMovementAnalyzer.class)) { Monitor.getOrNull(IAttractionBasedMovementAnalyzer.class).onAllNodeInitializationCompleted(moveableHosts); } } } /** * This default implementation relies on {@link PlacementModel}s to be * configured in the {@link TopologyFactory} */ @Override public void placeComponent(SimLocationActuator actuator) { // not supported } @Override public void changeTargetLocation(SimLocationActuator actuator, IAttractionPoint ap) { attractionAssigment.updateTargetAttractionPoint(actuator, ap); } protected void checkConfiguration() { if (localMovementStrategy == null) { throw new ConfigurationException( "LocalMovementStrategy is missing in ModularMovementModel!"); } if (attractionAssigment == null) { throw new ConfigurationException( "TransitionStrategy is missing in ModularMovementModel!"); } if(speedProvider == null) { throw new ConfigurationException( "SpeedDistributionProvider is missing in ModularMovementModel!"); } } @Override public void addComponent(SimLocationActuator comp) { moveableHosts.add(comp); if (!routeSensorComponents.containsKey(comp)) { routeSensorComponents.put(comp, new RouteSensorComponent(comp)); } } @Override public void updatedAttractionAssignment(SimLocationActuator component, IAttractionPoint newAssignment) { /* * Use this method to calculate the offset and target location for a * host. */ PositionVector attractionCenter = (PositionVector) newAssignment; PositionVector destination = null; /* * Even if an AP does not have a radius, we slightly offset */ double apRadius = (newAssignment.hasRadius() ? Math.max(newAssignment.getRadius(), 25.0) : 25.0); int tries = 0; do { destination = addOffset(attractionCenter, apRadius); // Check constraints if (!checkBoundaries(destination)) { destination = null; if (tries > 100) { throw new AssertionError("Unable to find a valid target destination within <100 tries."); } } tries++; } while (destination == null); currentTargets.put(component, destination); } protected void move() { for (SimLocationActuator component : moveableHosts) { assert currentTargets.containsKey(component); doLocalMovement(component, currentTargets.get(component)); } for (IAttractionPoint aps : getAttractionPoints()) { if(aps instanceof MobileAttractionPoint) { ((MobileAttractionPoint) aps).move(); } } Event.scheduleWithDelay(timeBetweenMoveOperation, this, null, EVENT_MOVE); } /** * 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 */ PositionVector offsetPosition = new PositionVector(position); 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)); offsetPosition.add(offset); return offsetPosition; } /** * * 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 */ protected void doLocalMovement(SimLocationActuator ms, PositionVector destination) { Either either = localMovementStrategy.nextPosition(ms, destination); if (either.hasLeft()) { ms.updateCurrentLocation(either.getLeft()); if(!checkBoundaries(ms.getRealPosition())) { System.err.println("Modular Movement Model: Host moved outside of simulated area!"); } } else { attractionAssigment.reachedAttractionPoint(ms, ms.getCurrentTargetAttractionPoint()); } } /* * ===================================================================================================== * === HELPER FUNCTIONS * ===================================================================================================== */ /** * Notifies the user if a hosts position lies outside of the specified world size. * Enable asserts to get the notification * */ public boolean checkBoundaries(PositionVector position) { return DefaultTopology.isWithinWorldBoundaries(position); } @Override public void eventOccurred(Object content, int type) { if (type == EVENT_INIT) { initialize(); } else if (type == EVENT_MOVE) { move(); } } @Override public ISpeedDistributionProvider getMovementSpeedDistribution() { return speedProvider; } /* * ===================================================================================================== * === GETTER AND SETTER FUNCTIONS * ===================================================================================================== */ public LinkedHashSet 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 setIAttractionProvider(IAttractionProvider attractionProvider) { if (attractionProvider == null) { throw new ConfigurationException( "AttractionProvider is missing in ModularMovementModel!"); } this.attractionProvider = attractionProvider; } public void setISpeedDistributionProvider(ISpeedDistributionProvider speedDistributionProvider) { if (speedDistributionProvider == null) { throw new ConfigurationException( "SpeedDistributionProvider is missing in ModularMovementModel!"); } this.speedProvider = speedDistributionProvider; } public void setLocalMovementStrategy(LocalMovementStrategy localMovementStrategy) { if (localMovementStrategy == null) { throw new ConfigurationException( "LocalMovementStrategy is missing in ModularMovementModel!"); } this.localMovementStrategy = localMovementStrategy; } public void setIAttractionAssigmentStrategy(IAttractionAssigmentStrategy transition) { if (transition == null) { throw new ConfigurationException( "IAttractionAssigmentStrategy is missing in ModularMovementModel!"); } this.attractionAssigment = transition; } public void setIMapVisualization(IMapVisualization mapVisualization) { this.mapVisualization = mapVisualization; } public void setModelVisualisation(ModularMovementModelViz modelVis) { modelVisualisation = modelVis; modelVisualisation.setMovementModel(this); } @Override public IAttractionPoint getTargetLocation(SimLocationActuator actuator) { return attractionAssigment.getAssignment(actuator); } /** * Only for visualization! * * @return */ public List getAttractionPoints() { return new Vector(attractionProvider.getAttractionPoints()); } public void setAttractionPointViz(AttractionPointViz viz) { this.attractionPointViz = viz; } public LocalMovementStrategy getLocalMovementStrategy() { return this.localMovementStrategy; } }