package de.tud.kom.p2psim.impl.topology.movement.modularosm; 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.local.RouteImpl; import de.tud.kom.p2psim.impl.topology.movement.local.RealWorldStreetsMovement; import de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.ITransitionStrategy; import de.tud.kom.p2psim.impl.util.Either; import de.tudarmstadt.maki.simonstrator.api.Binder; import de.tudarmstadt.maki.simonstrator.api.Monitor; import de.tudarmstadt.maki.simonstrator.api.NodeDebugMonitor; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint; import java.util.HashMap; import java.util.LinkedList; import java.util.List; /** * This class is meant to be used with the RealWorldStreetsMovement * and allows changes of the movement type and the used {@link ITransitionStrategy} mid-simulation * on a per-host basis. It acts like the {@link ModularMovementModel}, but each of the {@link SimLocationActuator}s * can have a different movement type and {@link ITransitionStrategy}. All routes and targets will be * calculated accordingly.

* * Originally the whole movement system within the simonstrator platform was not intended to be manipulable * by an application since only overlays were implemented which in a real world would reside on handheld devices * for example and should therefore not be able to manipulate the movement of the node/user. * But demand changed and for some systems it is inevitable to be able to control/influence the movement. Therefore * this class was created to fill the gap and provide access to the movement from outside the internal system. * * USAGE: * So, since the movement of a person in real life could not be controlled by anyone but the person itself, * the access to this class is only provided from the simrunner project since it is responsible for the simulation * of the "real-life" parts of the simonstrator platform. From within this project you have access to the * {@link de.tud.kom.p2psim.impl.topology.DefaultTopologyComponent} from where you can access this movement model. * If you want to use different movement types, all you have to do is * (besides selecting this model in your config) call the {@link #setMovementType(SimLocationActuator, String)} * for each of your components.
* * The used {@link ITransitionStrategy} can be changed on runtime, too. However, the first * TransitionStrategy specified in the config will be used as default, and will be applied if there is * no further strategy specified for a specific host. To use multiple strategies, add them to your * config just as the first one. To set a specific strategy for a specific host, call the {@link #setTransitionForComponent(SimLocationActuator, Class)} * with the second parameter being the class of the transition strategy you want to use. * * NOTE: All the movement types you are using need to be specified in you config for the * {@link RealWorldStreetsMovement}. E.g if you are using 'car' and 'foot' movement types, * in you config the MovementType for the {@link RealWorldStreetsMovement} should be specified as 'car,foot'. * If not done properly there won't be an error, but the movement behaviour will be strange. * * @author Clemens Krug */ public class ModularMultiTypeMovementModel extends ModularMovementModel { private HashMap movementTypes; private HashMap transitions; private HashMap supportedTransitions; private LinkedList movementListeners = new LinkedList<>(); /** * Suppresses notifications to {@link de.tud.kom.p2psim.impl.topology.movement.modularosm.transition.ITransitionStrategy.AttractionAssignmentListener}s. */ private boolean suppressListenerNotify = false; public ModularMultiTypeMovementModel() { super(); movementTypes = new HashMap<>(); transitions = new HashMap<>(); supportedTransitions = new HashMap<>(); } @Override public void initialize() { super.initialize(); suppressListenerNotify = true; for(ITransitionStrategy strategy : supportedTransitions.values()) { strategy.setAttractionPoints(transition.getAllAttractionPoints()); strategy.addAttractionAssignmentListener(this); for (SimLocationActuator ms : moveableHosts) { strategy.addComponent(ms); } } suppressListenerNotify = false; } @Override protected void doLocalMovement(SimLocationActuator ms, PositionVector destination) { assert localMovementStrategy instanceof RealWorldStreetsMovement: "ModularMultiTypeMovementModel can only be used with RealWorldStreetsMovement!"; Either either; if(movementTypes.containsKey(ms)) either = ((RealWorldStreetsMovement) localMovementStrategy).nextPosition(ms, destination, movementTypes.get(ms)); else either = localMovementStrategy.nextPosition(ms, destination); if (either.hasLeft()) { ms.updateCurrentLocation(either.getLeft()); /* * Check for negative or out of bound coordinates! */ assert ms.getRealPosition().getX() >= 0.0 && ms.getRealPosition().getX() <= Binder .getComponentOrNull(Topology.class) .getWorldDimensions().getX(); assert ms.getRealPosition().getY() >= 0.0 && ms.getRealPosition().getY() <= Binder .getComponentOrNull(Topology.class) .getWorldDimensions().getY(); } else { if(transitions.containsKey(ms)) { transitions.get(ms).reachedAttractionPoint(ms); } else { transition.reachedAttractionPoint(ms); } movementListeners.forEach(l -> l.onTransition(ms)); } } /** * Sets the movement type for the specified {@link SimLocationActuator}. Movement types can be for example * 'car' or 'foot'. Used types need to be specified in the config at the {@link RealWorldStreetsMovement}. * @param ms The SimLocationActuator * @param movementType the movement type */ public void setMovementType(SimLocationActuator ms, String movementType) { movementTypes.put(ms, movementType); } /** * Returns the current movement type for this {@link SimLocationActuator} as String. * @param ms The SimLocationActuator * @return the current movement type */ public String getMovementType(SimLocationActuator ms) { return movementTypes.get(ms); } /** * Return the currently used transitions strategy of the specified component * @param ms the component * @return the current transition strategy */ public ITransitionStrategy getTransitionForComponent(SimLocationActuator ms) { return transitions.get(ms); } /** * Gets one of the supported transition strategies. * @param strategy The class of the strategy which should be returned. * @return The specified strategy */ public ITransitionStrategy getTransitionStrategy(Class strategy) { ITransitionStrategy selectedStrategy = supportedTransitions.get(strategy); if(selectedStrategy == null) { throw new UnsupportedOperationException( String.format("ModularMultiTypeMovementModel: TransitionStrategy %s ist not supported!", strategy.toString())); } return selectedStrategy; } /** * Sets the {@link ITransitionStrategy} for the specified {@link SimLocationActuator}. Used strategies * need to be registered in the config. * @param ms The SimLocationActuator * @param strategy the strategy to use */ public void setTransitionForComponent(SimLocationActuator ms, Class strategy) { changeTransitionStrategy(ms, getTransitionStrategy(strategy)); } /** * Changes the transition strategy of the specified {@link SimLocationActuator}. * @param ms The SimLocationActuator * @param newStrategy the new strategy to use */ private void changeTransitionStrategy(SimLocationActuator ms, ITransitionStrategy newStrategy) { ITransitionStrategy usedStrategy = transitions.containsKey(ms) ? transitions.get(ms) : transition; newStrategy.updateTargetAttractionPoint(ms, usedStrategy.getAssignment(ms)); transitions.put(ms, newStrategy); Monitor.log(ModularMultiTypeMovementModel.class, Monitor.Level.DEBUG, String.format("Client %s changed his transition strategy from %s to %s", ms.getHost().getId().toString(), usedStrategy.getClass(), newStrategy.getClass())); } /** * Returns a list of points representing the current route of the component. Points are * in x / y values of the own world. * @param ms the component * @return list of movement points. */ public List getMovementPoints(SimLocationActuator ms) { return ((RealWorldStreetsMovement) localMovementStrategy).getMovementPoints(ms); } public RealWorldStreetsMovement getMovementStrategy() { return (RealWorldStreetsMovement) localMovementStrategy; } /** * Sets the default {@link ITransitionStrategy} for the specified {@link SimLocationActuator}. * @param ms The SimLocationActuator */ public void returnToDefaultTransition(SimLocationActuator ms) { transitions.remove(ms); } @Override public void changeTargetLocation(SimLocationActuator actuator, AttractionPoint ap) { if(transitions.containsKey(actuator)) transitions.get(actuator).updateTargetAttractionPoint(actuator, ap); else transition.updateTargetAttractionPoint(actuator, ap); } @Override public void setITransitionStrategy(ITransitionStrategy transition) { if(supportedTransitions.size() == 0) this.transition = transition; supportedTransitions.put(transition.getClass(), transition); } @Override public AttractionPoint getTargetLocation(SimLocationActuator actuator) { if(transitions.containsKey(actuator)) return transitions.get(actuator).getAssignment(actuator); else return transition.getAssignment(actuator); } @Override public void updatedAttractionAssignment(SimLocationActuator component, AttractionPoint newAssignment) { //Notifications of listeners get suppressed in setup phase to prevent multiple assignments of destinations. if(suppressListenerNotify) return; super.updatedAttractionAssignment(component, newAssignment); } public void addMovementListener(MultiTypeMovementListener listener) { movementListeners.add(listener); } public void removeMovementListener(MultiTypeMovementListener listener) { movementListeners.remove(listener); } }