Commit dfddaec2 authored by Clemens Krug's avatar Clemens Krug
Browse files

Movement model allows transitions switches

+ Added the possibility to switch the transition mid-simulation for the ModularMultiTypeMovement
+ Added a new transition strategy
parent 65563542
......@@ -113,7 +113,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
private ModularMovementModelViz modelVisualisation;
private Set<SimLocationActuator> moveableHosts = new LinkedHashSet<SimLocationActuator>();
protected Set<SimLocationActuator> moveableHosts = new LinkedHashSet<SimLocationActuator>();
private Map<SimLocationActuator, PositionVector> currentTarget = new LinkedHashMap<>();
......
......@@ -2,24 +2,35 @@ 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.api.topology.movement.local.LocalMovementStrategy;
import de.tud.kom.p2psim.impl.topology.PositionVector;
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.component.sensor.location.AttractionPoint;
import java.util.HashMap;
import java.util.HashSet;
/**
* This class is meant to be used with the RealWorldStreetsMovement
* if different movement types should be used simultaneously. It acts
* like the {@link ModularMovementModel}, but each of the {@link SimLocationActuator}s
* can have a different movement type and the movement route will be calculated according
* to this type. <BR><BR>
* 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 target will be
* calculated accordingly. <BR><BR>
*
* USAGE:
* So basically if you want to use this movement model, all you have to do is
* (besides specifying it in your config) call the {@link #setMovementType(SimLocationActuator, String)}
* for each of your components.<BR><BR>
* If you want to use different movement types, all you have to do is
* (besides selecting this model it in your config) call the {@link #setMovementType(SimLocationActuator, String)}
* for each of your components.<BR>
*
* 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,
......@@ -31,13 +42,31 @@ import java.util.HashMap;
public class ModularMultiTypeMovementModel extends ModularMovementModel
{
private HashMap<SimLocationActuator, String> movementTypes;
private HashMap<SimLocationActuator, ITransitionStrategy> transitions;
private HashMap<Class, ITransitionStrategy> supportedTransitions;
public ModularMultiTypeMovementModel()
{
super();
movementTypes = new HashMap<>();
transitions = new HashMap<>();
supportedTransitions = new HashMap<>();
}
@Override
public void initialize()
{
super.initialize();
for(ITransitionStrategy strategy : supportedTransitions.values())
{
strategy.setAttractionPoints(transition.getAllAttractionPoints());
strategy.addAttractionAssignmentListener(this);
for (SimLocationActuator ms : moveableHosts) {
strategy.addComponent(ms);
}
}
}
@Override
protected void doLocalMovement(SimLocationActuator ms, PositionVector destination)
......@@ -62,7 +91,8 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel
.getComponentOrNull(Topology.class)
.getWorldDimensions().getY();
} else {
transition.reachedAttractionPoint(ms);
if(transitions.containsKey(ms)) transitions.get(ms).reachedAttractionPoint(ms);
else transition.reachedAttractionPoint(ms);
}
}
......@@ -75,4 +105,47 @@ public class ModularMultiTypeMovementModel extends ModularMovementModel
{
return movementTypes.get(ms);
}
public void setTransitionForComponent(SimLocationActuator ms, Class strategy)
{
ITransitionStrategy selectedStrategy = supportedTransitions.get(strategy);
if(selectedStrategy == null)
{
throw new UnsupportedOperationException(
String.format("ModularMultiTypeMovementModel: TransitionStrategy %s ist not supported!", strategy.toString()));
}
changeTransitionStrategy(ms, selectedStrategy);
}
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()));
}
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);
}
}
......@@ -20,19 +20,18 @@ import java.util.Map;
* the first thing to do is specify this {@link ModularMovementIconVis} as visualisation for
* your modular movement model. To add icons to the visualisation the class {@link IconMapping} is used within the tags.
* You have two tags available for different kind of icons:<BR><BR>
*
* <p>
* 1. Class icons: These icons are used dependent on the class of the components the host includes. You can
* add a class icon by using the &lt;ClassIcon> tag you config and setting the fully qualified classname
* of you components as object. If a host has multiple components with different specified icons, the first one will be used.<BR><BR>
*
* <p>
* 2. State icons: These icons are used dependent on the state of a host and take precedence over the class icons. You
* can add them via the &lt;StateIcon> tag. The object should be a String representing the state. When
* using these types of icons, you need to set a {@link StateSelector} for this class in the config.<BR><BR>
*
* <p>
* For example usage, see the movement_social.xml config for the resourceAllocation overlay.
*
* @author Clemens Krug
*/
public class ModularMovementIconVis extends ModularMovementModelViz implements EventHandler
{
......@@ -65,24 +64,28 @@ public class ModularMovementIconVis extends ModularMovementModelViz implements E
VisualizationTopologyView.VisualizationInjector.getTopologyView().setShowNodes(false);
for(Map.Entry<Class, String> e : pathMap.entrySet())
for (Map.Entry<Class, String> e : pathMap.entrySet())
{
try
{
try {
Image icon = ImageIO.read(new File(e.getValue())).getScaledInstance(-1, 15, Image.SCALE_FAST);
classIconMap.put(e.getKey(), icon);
} catch (IOException ioe) {
} catch (IOException ioe)
{
Monitor.log(ModularMovementIconVis.class, Monitor.Level.WARN, "Could not load icon from path %s", e.getValue());
}
}
for(Map.Entry<String, String> e : stateMap.entrySet())
for (Map.Entry<String, String> e : stateMap.entrySet())
{
assert stateSelector != null : "There must be a state selector specified when using state icons!";
try {
try
{
Image icon = ImageIO.read(new File(e.getValue())).getScaledInstance(-1, 15, Image.SCALE_FAST);
stateIconMap.put(e.getKey(), icon);
} catch (IOException ioe) {
} catch (IOException ioe)
{
Monitor.log(ModularMovementIconVis.class, Monitor.Level.WARN, "Could not load icon from path %s", e.getValue());
}
}
......@@ -91,71 +94,89 @@ public class ModularMovementIconVis extends ModularMovementModelViz implements E
}
@Override
public void paint(Graphics g) {
public void paint(Graphics g)
{
super.paintSuper(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (showAttractionPoints) {
if (showAttractionPoints)
{
super.drawAttractionPoints(g2);
}
if (showNodePositions) {
if (showNodePositions)
{
for (SimLocationActuator comp : movementModel
.getAllLocationActuators()) {
.getAllLocationActuators())
{
drawNodePosition(g2, comp);
}
}
}
@Override
protected void drawNodePosition(Graphics2D g2, SimLocationActuator comp)
{
boolean iconNotFound = false;
boolean iconNotFound = true;
Host host = comp.getHost();
Point2D pt = comp.getRealPosition().asPoint();
g2.setColor(Color.GRAY);
//Check if the component has a specified state, as this would have precedence over the classIcon.
// Check if the component has a specified state, as this would have precedence over the classIcon.
if (stateSelector != null)
{
String state = stateSelector.getState(comp);
if(stateIconMap.containsKey(state))
if (stateIconMap.containsKey(state))
{
g2.drawImage(stateIconMap.get(state), (int) pt.getX(), (int) pt.getY(), null);
Image icon = stateIconMap.get(state);
int width = icon.getWidth(null);
int height = icon.getHeight(null);
g2.drawImage(icon, (int) pt.getX() - width / 2, (int) pt.getY() - height / 2, null);
return;
}
}
// If not, check if there's a class icon available
for(Map.Entry<Class, Image> e : classIconMap.entrySet())
for (Map.Entry<Class, Image> e : classIconMap.entrySet())
{
try
{
try {
host.getComponent(e.getKey());
g2.drawImage(e.getValue(), (int) pt.getX(), (int) pt.getY(), null);
Image icon = e.getValue();
int width = icon.getWidth(null);
int height = icon.getHeight(null);
g2.drawImage(icon, (int) pt.getX() - width / 2, (int) pt.getY() - height / 2, null);
iconNotFound = false;
break;
} catch (ComponentNotAvailableException nae) {
} catch (ComponentNotAvailableException nae)
{
iconNotFound = true;
}
}
// If not, use standard visualisation
if(iconNotFound) super.drawNodePosition(g2, comp);
if (iconNotFound) super.drawNodePosition(g2, comp);
}
@Override
public void eventOccurred(Object content, int type) {
if (type == 0) {
public void eventOccurred(Object content, int type)
{
if (type == 0)
{
initialize();
}
}
public void setClassIcon(IconMapping classIcon)
{
try {
try
{
pathMap.put(Class.forName(classIcon.getObject()), classIcon.getIconpath());
} catch (ClassNotFoundException e) {
} catch (ClassNotFoundException e)
{
Monitor.log(ModularMovementIconVis.class, Monitor.Level.WARN, "Class %s not found!", classIcon.getObject());
}
}
......
......@@ -16,6 +16,7 @@ public class ManualAssignmentStrategy implements ITransitionStrategy
private List<AttractionAssignmentListener> listeners = new LinkedList<>();
@Override
public AttractionPoint getAssignment(SimLocationActuator comp)
{
......@@ -52,12 +53,11 @@ public class ManualAssignmentStrategy implements ITransitionStrategy
assignments.put(ms, aPoint);
}
listeners.forEach(listener -> listener.updatedAttractionAssignment(ms, assignments.get(ms)));
}
@Override
public void reachedAttractionPoint(SimLocationActuator ms) {
//Nothing to do.
}
@Override
......
package de.tud.kom.p2psim.impl.topology.movement.modularosm.transition;
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;
import java.util.*;
/**
* Created by Clemens on 15.02.2017.
*/
public class RandomInAreaTransitionStrategy implements ITransitionStrategy
{
private Random random;
private LinkedHashSet<AttractionPoint> aPoints = new LinkedHashSet<>();
private Map<SimLocationActuator, AttractionPoint> assignments = new HashMap<>();
private Map<SimLocationActuator, AttractionPoint> currentTarget = new HashMap<>();
private List<AttractionAssignmentListener> listeners = new LinkedList<>();
private int defaultRadius;
public RandomInAreaTransitionStrategy()
{
random = Randoms.getRandom(this.getClass());
}
@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<AttractionPoint> attractionPoints) {
aPoints.addAll(attractionPoints);
}
@Override
public Set<AttractionPoint> 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));
}
listeners.forEach(listener -> listener.updatedAttractionAssignment(ms, currentTarget.get(ms)));
}
@Override
public void reachedAttractionPoint(SimLocationActuator ms) {
currentTarget.put(ms, nextRandomPosition(assignments.get(ms), defaultRadius));
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, defaultRadius));
listeners.forEach(listener -> listener.updatedAttractionAssignment(comp, attractionPoint));
}
public void updateTargetAttractionPoint(SimLocationActuator comp, AttractionPoint attractionPoint, int radius)
{
assignments.put(comp, attractionPoint);
currentTarget.put(comp, nextRandomPosition(attractionPoint, radius));
listeners.forEach(listener -> listener.updatedAttractionAssignment(comp, attractionPoint));
}
public void setDefaultRadius(int radius)
{
this.defaultRadius = radius;
}
private BasicAttractionPoint nextRandomPosition(Location center, int radius)
{
double x = center.getLongitude();
double y = center.getLatitude();
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));
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment