Commit aa205068 authored by Nils Richerzhagen's avatar Nils Richerzhagen
Browse files

Merge branch 'nr/resAlloc-clemens' into 'nr/monitoring-req-resp'

Merge Nr/res alloc clemens into nr/monitoring-req-resp

See merge request !10
parents fb48b989 28159a91
......@@ -340,7 +340,7 @@
<dependency>
<groupId>com.graphhopper</groupId>
<artifactId>graphhopper</artifactId>
<version>0.5.0</version>
<version>0.7.0</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
......
package de.tud.kom.p2psim.api.application;
/**
* The workload items which should be generated by a {@link WorkloadGenerator}.
* More or less just a wrapper to pass a specific request to the {@link WorkloadListener}s.
*/
public class WorkloadItem<T>
{
private T content;
public WorkloadItem(T content)
{
this.content = content;
}
/**
* Returns the content of this workload item
* @return the content
*/
public T getContent()
{
return content;
}
}
......@@ -20,7 +20,6 @@
package de.tud.kom.p2psim.api.application;
import de.tud.kom.p2psim.api.common.SimHost;
/**
* This is the Listener for an application, to get the call to do something.
......@@ -36,13 +35,20 @@ import de.tud.kom.p2psim.api.common.SimHost;
*
* @author Christoph Muenker
* @version 1.0, 14.06.2013
*
*
*
* 12.03.2017 Clemens Krug:
* The getApplication method has been removed since it did not comply to the
* typical Observer/Listener pattern. Instead {@link #doWork(WorkloadItem)} was added
* and the {@link Application}s should now handle the workloads accordingly.
*/
public interface WorkloadListener {
public interface WorkloadListener
{
/**
* Gets the Application, which uses this Listener
*
* @return The application which is using the listener.
* Called when a {@link WorkloadItem} is ready.
* @param item The workload item.
*/
public Application getApplication();
void doWork(WorkloadItem item);
}
package de.tud.kom.p2psim.api.common;
/**
* Created by Clemens on 02.04.2017.
*/
public interface NodeDebugUpdateListener
{
}
package de.tud.kom.p2psim.impl.common;
import de.tud.kom.p2psim.impl.topology.views.visualization.ui.NodeDebugVisualisation;
import de.tudarmstadt.maki.simonstrator.api.common.graph.INodeID;
import de.tudarmstadt.maki.simonstrator.api.component.core.NodeDebugMonitorComponent;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
/**
* Default node debug monitor. Provides logging capabilities and data access as well
* as the option to define filters on which information should be logged.
* Per node information can be shown by simply clicking on the desired node.
* Filters can be used as blacklist or as whitelist and
* are specified via the xml config. <BR><BR>
*
* How to define Filters:<BR>
* The standard XML configuration looks like this:<BR><BR>
* &lt;DebugMonitor class="de.tud.kom.p2psim.impl.common.DefaultNodeDebugMonitor" static="getInstance" enableVis="$ENABLE_DEBUG_VIS"&gt;<BR>
* &#09;&lt;PackageFilter class="de.tud.kom.p2psim.impl.common.PackageFilter"<BR>
* &#09;&#09;type="EXCLUDE"<BR>
* &#09;&#09;packageName="de.tudarmstadt.maki.simonstrator.peerfact.application.resAlloc.user"/&gt<BR>
* &lt;/DebugMonitor&gt;<BR><BR>
*
* Any package or class can be specified as filter and all
* subpackages and classes will be filtered accordingly. For types you can use 'EXCLUDE' or 'INCLUDE'
* depending on if you want to use whitelisting or blacklisting. Multiple
* &lt;PackageFilter&gt; tags can be defined, however you can't use INCLUDE and EXCLUDE simultaneously.
*/
public class DefaultNodeDebugMonitor implements NodeDebugMonitorComponent
{
private static DefaultNodeDebugMonitor instance;
private HashMap<INodeID, HashMap<Class, HashMap<String, Object>>> data = new HashMap<>();
private LinkedList<NodeDebugUpdateListener> listeners = new LinkedList<>();
//Marker to prevent creation of two visualisation components
private boolean visAlreadyEnabled = false;
//Marker determining if blacklisting or whitelisting is enabled
private boolean filterInitialised = false;
private boolean whitelist = false;
private LinkedList<PackageFilter> filteredPackages = new LinkedList<>();
private HashMap<Class, Boolean> filteredClasses = new HashMap<>();
public static DefaultNodeDebugMonitor getInstance()
{
if(instance == null)
{
instance = new DefaultNodeDebugMonitor();
}
return instance;
}
@Override
public void update(Class<?> subject, INodeID nodeID, String entry, Object value)
{
if(filteredClasses.containsKey(subject))
{
//If Whitelisting and class not contained in filter
if(whitelist && !filteredClasses.get(subject)) return;
//If blacklisting and class contained in filter.
if(!whitelist && filteredClasses.get(subject)) return;
}
HashMap<Class, HashMap<String, Object>> nodeData;
if(data.containsKey(nodeID)) nodeData = data.get(nodeID);
else {
nodeData = new HashMap<>();
data.put(nodeID, nodeData);
}
HashMap<String, Object> nodeClassData;
if(nodeData.containsKey(subject)) nodeClassData = nodeData.get(subject);
else
{
//First update for this class. Check if the class needs to be filtered.
boolean classFiltered = isFiltered(subject);
filteredClasses.put(subject, classFiltered);
//If the class is excluded, don't add the information and return.
if((whitelist && !classFiltered) || (!whitelist && classFiltered) ) return;
nodeClassData = new HashMap<>();
nodeData.put(subject, nodeClassData);
}
nodeClassData.put(entry, value);
listeners.forEach(l -> l.onNodeDebugUpdate(subject, nodeID, entry, value));
}
@Override
public Map<Class, HashMap<String, Object>> getNodeData(INodeID nodeID) {
if(data.containsKey(nodeID)) return data.get(nodeID);
else return new HashMap<>();
}
@Override
public void addUpdateListener(NodeDebugUpdateListener listener) {
listeners.add(listener);
}
@Override
public void removeUpdateListener(NodeDebugUpdateListener listener) {
listeners.remove(listener);
}
@Override
public void setEnableVis(boolean enabled) {
if(enabled && !visAlreadyEnabled) new NodeDebugVisualisation();
}
/**
* Adds a filter to the debug monitor. Should only be used via XML config and
* is not meant to be only called while simulation setup.
* @param filter the filter to add
*/
public void setPackageFilter(PackageFilter filter)
{
//First entry sets whitelisting/blacklisting
if(!filterInitialised)
{
filterInitialised = true;
whitelist = filter.isWhitelist();
}
// If the added type is not conform with the type of listing, an error is thrown.
if(filter.isWhitelist() != whitelist) throw new AssertionError("Can't use Includes/Excludes simultaneously!");
//Don't save filters for packages where a parent package is already filtered.
for(PackageFilter pf : filteredPackages)
{
if(filter.subPackageOf(pf)) return;
if(pf.subPackageOf(filter))
{
filteredPackages.remove(pf);
filteredPackages.add(filter);
return;
}
}
filteredPackages.add(filter);
}
/**
* Check if a class is contained in the filter.
* @param c the class to check
* @return true if contained, false otherwise.
*/
private boolean isFiltered(Class c)
{
for(PackageFilter pf : filteredPackages)
{
if(pf.containsClass(c)) return true;
}
return false;
}
}
package de.tud.kom.p2psim.impl.common;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* Created by Clemens on 29.04.2017.
*/
public class PackageFilter
{
private String packageName;
private TYPE type;
@XMLConfigurableConstructor({"packageName", "type"})
public PackageFilter(String packageName, String type)
{
this.packageName = packageName;
this.type = TYPE.valueOf(type.toUpperCase());
}
private String getPackageName()
{
return packageName;
}
public boolean isWhitelist()
{
return type == TYPE.INCLUDE;
}
public boolean subPackageOf(PackageFilter filter)
{
return filter.getPackageName().contains(packageName);
}
public boolean containsClass(Class c)
{
return c.getName().contains(packageName);
}
public enum TYPE
{
INCLUDE,
EXCLUDE
}
}
......@@ -737,7 +737,7 @@ public class DefaultConfigurator implements Configurator {
if (cArgs.length != types.length)
throw new ConfigurationException(
"The size of the argument list of the XML configurable constructor ("
+ cArgs
+ Arrays.toString(cArgs)
+ ") is unequal to the size of arguments of the constructor is was applied to.");
// Constructor can be called with the given XML
......
......@@ -32,6 +32,7 @@ import de.tud.kom.p2psim.api.topology.obstacles.ObstacleModel;
import de.tud.kom.p2psim.api.topology.social.SocialView;
import de.tud.kom.p2psim.api.topology.views.TopologyView;
import de.tud.kom.p2psim.api.topology.waypoints.WaypointModel;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.GPSCalculation;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tud.kom.p2psim.impl.topology.views.visualization.world.SocialViewComponentVis;
......@@ -63,6 +64,8 @@ public class DefaultTopology implements Topology {
public DefaultTopology(PositionVector worldDimensions) {
this.worldDimensions = worldDimensions;
components = new LinkedList<TopologyComponent>();
// obstacles = new LinkedList<Obstacle>();
topoListeners = new LinkedList<TopologyListener>();
......
......@@ -38,13 +38,7 @@ import de.tud.kom.p2psim.api.topology.movement.MovementModel;
import de.tud.kom.p2psim.api.topology.placement.PlacementModel;
import de.tud.kom.p2psim.api.topology.views.TopologyView;
import de.tud.kom.p2psim.impl.simengine.Simulator;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Graphs;
import de.tudarmstadt.maki.simonstrator.api.Host;
import de.tudarmstadt.maki.simonstrator.api.Oracle;
import de.tudarmstadt.maki.simonstrator.api.Randoms;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.*;
import de.tudarmstadt.maki.simonstrator.api.common.graph.Graph;
import de.tudarmstadt.maki.simonstrator.api.common.graph.IEdge;
import de.tudarmstadt.maki.simonstrator.api.common.graph.INode;
......@@ -263,6 +257,8 @@ public class DefaultTopologyComponent implements TopologyComponent {
@Override
public void updateCurrentLocation(Location location) {
position.set(location);
NodeDebugMonitor.update(this.getClass(), getHost().getId(), "Current Location", location);
NodeDebugMonitor.update(this.getClass(), getHost().getId(), "Distance to target", location.distanceTo(movementModel.getTargetLocation(this)));
// notify "non-request" listeners
for (LocationListener locationListener : listeners) {
locationListener.onLocationChanged(getHost(), getLastLocation());
......@@ -273,6 +269,8 @@ public class DefaultTopologyComponent implements TopologyComponent {
public void setTargetAttractionPoint(AttractionPoint targetAttractionPoint)
throws UnsupportedOperationException {
movementModel.changeTargetLocation(this, targetAttractionPoint);
NodeDebugMonitor.update(this.getClass(), getHost().getId(), "Target Location", targetAttractionPoint);
}
@Override
......
......@@ -20,10 +20,6 @@
package de.tud.kom.p2psim.impl.topology.movement.local;
import java.util.HashMap;
import java.util.Locale;
import java.util.UUID;
import com.graphhopper.GHRequest;
import com.graphhopper.GHResponse;
import com.graphhopper.GraphHopper;
......@@ -31,19 +27,26 @@ import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.util.PointList;
import com.graphhopper.util.shapes.GHPoint;
import com.graphhopper.util.shapes.GHPoint3D;
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.GPSCalculation;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView;
import de.tud.kom.p2psim.impl.util.Either;
import de.tud.kom.p2psim.impl.util.Left;
import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Monitor.Level;
import java.util.HashMap;
import java.util.Locale;
import java.util.UUID;
/**
* This movement strategy uses the data from osm and navigates the nodes throught streets to the destination
*
* 13.03.2017 Clemens Krug: Fixed a bug. When the GraphHopper routing had errors the nodes would move to the
* top right corner and not, as intended, straight to their destination.
*
* @author Martin Hellwig
* @version 1.0, 07.07.2015
......@@ -59,13 +62,14 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
private String osmFileLocation; //use pbf-format, because osm-format causes problems (xml-problems)
private String graphFolderFiles;
private String movementType; //car, bike or foot
private String navigationalType; //fastest,
private double latLeft; //Values from -90 to 90; always smaller than latRight
private double latRight; //Values from -90 to 90
private String defaultMovement;
private String navigationalType; //fastest,
private double latLower; //Values from -90 to 90; always smaller than latUpper
private double latUpper; //Values from -90 to 90
private double lonLeft; //Values from -180 to 180; Always smaller than lonRight
private double lonRight; //Values from -180 to 180
private boolean uniqueFolders;
/**
* Tolerance in meters (if the node reached a waypoint up to "tolerance"
* meters, it will select the next waypoint in the path.
......@@ -75,8 +79,8 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
public RealWorldStreetsMovement() {
this.worldDimensions = Binder.getComponentOrNull(Topology.class)
.getWorldDimensions();
latLeft = GPSCalculation.getLatLower();
latRight = GPSCalculation.getLatUpper();
latLower = GPSCalculation.getLatLower();
latUpper = GPSCalculation.getLatUpper();
lonLeft = GPSCalculation.getLonLeft();
lonRight = GPSCalculation.getLonRight();
}
......@@ -84,6 +88,7 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
private void init() {
hopper = new GraphHopper().forServer();
hopper.setOSMFile(osmFileLocation);
// where to store graphhopper files?
if (uniqueFolders) {
Monitor.log(RealWorldStreetsMovement.class, Level.WARN,
......@@ -97,16 +102,26 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
}
hopper.setEncodingManager(new EncodingManager(movementType));
hopper.importOrLoad();
init = true;
}
public Either<PositionVector, Boolean> nextPosition(SimLocationActuator comp, PositionVector destination)
{
return nextPosition(comp, destination, defaultMovement);
}
public Either<PositionVector, Boolean> nextPosition(SimLocationActuator comp,
PositionVector destination) {
if(!init) init();
PositionVector destination, String movementType) {
if(movementType == null || movementType.equals("") || !this.movementType.contains(movementType))
throw new AssertionError("Invalid movement type: " + movementType);
if(!init) init();
PositionVector newPosition = null;
if (destination
.distanceTo(comp.getRealPosition()) > getMovementSpeed(comp)) {
if (destination.distanceTo(comp.getRealPosition()) > getMovementSpeed(comp))
{
//if not set already for this node or new destination is different than last one
PointList pointList;
if(!movementPoints.containsKey(comp) || destination.distanceTo(movementPoints.get(comp).getDestination()) > 1.0) {
......@@ -119,15 +134,16 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
GHResponse rsp = hopper.route(req);
//If the requested point is not in the map data, simple return the destination as next point
if(rsp.hasErrors()) {
System.err.println("Requested Points (" + startPosition[0] + ", " + startPosition[1]
+ ") or (" + destinationPosition[0] + ", " + destinationPosition[1] + ") are out of the bounding box.");
Monitor.log(this.getClass(), Monitor.Level.ERROR, "Routing request for Host %s with starting point (" +
"%f,%f), destination (%f,%f) and type %s failed with error: %s.", comp.getHost().getId().valueAsString(),startPosition[0], startPosition[1],
destinationPosition[0], destinationPosition[1], movementType, rsp.getErrors());
pointList = new PointList();
pointList.add(new GHPoint(destination.getLatitude(), destination.getLongitude()));
pointList.add(new GHPoint(destinationPosition[0], destinationPosition[1]));
movementPoints.put(comp, new RealWorldMovementPoints(comp.getRealPosition(), destination, pointList, 0));
}
else {
pointList = rsp.getPoints();
pointList = rsp.getBest().getPoints();
movementPoints.put(comp, new RealWorldMovementPoints(comp.getRealPosition(), destination, pointList, 0));
}
}
......@@ -163,7 +179,7 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
*/
private double[] transformOwnWorldWindowToGPS(double x, double y) {
double[] gps_coordinates = new double[2];
gps_coordinates[0] = latLeft + (latRight - latLeft) * (worldDimensions.getY() - y)/worldDimensions.getY();
gps_coordinates[0] = latLower + (latUpper - latLower) * (worldDimensions.getY() - y)/worldDimensions.getY();
gps_coordinates[1] = lonLeft + (lonRight - lonLeft) * x/worldDimensions.getX();
return gps_coordinates;
}
......@@ -175,8 +191,9 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
* @return The projected position in world-dimensions
*/
private PositionVector transformGPSWindowToOwnWorld(double lat, double lon) {
VisualizationTopologyView.VisualizationInjector.getWorldX();
double x = worldDimensions.getX() * (lon - lonLeft)/(lonRight - lonLeft);
double y = worldDimensions.getY() - worldDimensions.getY() * (lat - latLeft)/(latRight - latLeft);
double y = worldDimensions.getY() - worldDimensions.getY() * (lat - latLower)/(latUpper - latLower);
x = Math.max(0, x);
x = Math.min(worldDimensions.getX(), x);
y = Math.max(0, y);
......@@ -194,6 +211,7 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
public void setMovementType(String movementType) {
this.movementType = movementType;
defaultMovement = movementType.split(",")[0];
}
public void setNavigationalType(String navigationalType) {
......@@ -203,13 +221,13 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
public void setWaypointTolerance(double tolerance) {
this.tolerance = tolerance;
}
/**
* For large batch simulations, we need to prevent same-time access to
* garphhopper temp data. Therefore, this flag creates unique folders for
* each run (which, obviously, wastes a lot of space and comp-resources and
* should not be used in standalone, single-threaded demo mode...)
*
*
* @param uniqueFolders
*/
public void setCreateUniqueFolders(boolean uniqueFolders) {
......
......@@ -25,7 +25,15 @@ import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.Visualiza
import de.tudarmstadt.maki.simonstrator.api.Binder;
/**
*
*
* CHANGELOG
* - 26.06.2017 Clemens Krug:
* Change some calculations that used numbers that were not comprehensible and not documented.
* In the previous version, changing the zoom of the visualisation / map would fuck up the whole
* positioning calculations. This has been fixed now.
*
*
*
* @author Martin Hellwig
* @version 1.0, Nov 3, 2015
*/
......@@ -37,7 +45,9 @@ public class GPSCalculation {
private static int zoom;
private static double scaleFactor;
private static double metersPerPixel;
private static int EARTH_CIRCUMFERENCE = 40030173;
/*
* http://gis.stackexchange.com/questions/2951/algorithm-for-offsetting-a-
......@@ -64,7 +74,8 @@ public class GPSCalculation {
* is valid on all zoom levels.
*/
// 17: 2, 16: 1, 15: 0.5, 14: 0.25
VisualizationInjector.setScale(Math.pow(2.0d, (zoom - 16)));
// VisualizationInjector.setScale(Math.pow(2.0d, (zoom - 16)));
VisualizationInjector.setScale(1/metersPerPixel);
}
public static double getLatCenter() {
......@@ -79,46 +90,34 @@ public class GPSCalculation {
return zoom;
}
public static double getLatUpper() {
return latCenter + (Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getY() / 111111d) * scaleFactor;
public static double getMetersPerPixel()
{
return metersPerPixel;
}
// return latCenter + scaleFactor * 0.027613 * Binder
// .getComponentOrNull(Topology.class).getWorldDimensions().getY()
// / 1000;
public static double getLatUpper() {
return latCenter + ((Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getY()/2) / 111111d);
}
public static double getLatLower() {
return latCenter - (Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getY() / 111111d) * scaleFactor;
// return latCenter - scaleFactor * 0.027613 * Binder
// .getComponentOrNull(Topology.class).getWorldDimensions().getY()
// / 1000;
return latCenter - ((Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getY()/2) / 111111d);
}
public static double getLonLeft() {
return lonCenter - (Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getX() / 111111d / scaleLon) * scaleFactor;
// return lonCenter - scaleFactor * 0.043 * Binder
// .getComponentOrNull(Topology.class).getWorldDimensions().getX()
// / 1000;
return lonCenter - ((Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getX()/2) / (111111d * scaleLon));
}
public static double getLonRight() {
return lonCenter + (Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getX() / 111111d / scaleLon) * scaleFactor;
// return lonCenter + scaleFactor * 0.043 * Binder
// .getComponentOrNull(Topology.class).getWorldDimensions().getX()
// / 1000;
return lonCenter + ((Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getX()/2) / (111111d * scaleLon));
}
public void setLatCenter(double latCenter) {
this.latCenter = latCenter;
this.scaleLon = Math.cos(Math.toRadians(latCenter));
this.scaleFactor = 0.38d;
}
public void setLonCenter(double lonCenter) {
......@@ -126,7 +125,8 @@ public class GPSCalculation {
}
public void setZoom(int zoom) {
this.zoom = zoom;
GPSCalculation.zoom = zoom;
GPSCalculation.metersPerPixel = EARTH_CIRCUMFERENCE * Math.cos(Math.toRadians(GPSCalculation.getLatCenter())) / Math.pow(2, GPSCalculation.getZoom() + 8);
setScaleFactor();
}
}
......@@ -65,7 +65,7 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.LocationAc
* M1: A general {@link MovementModel} is not used, because we use static
* attraction points.
* <p>
* M2: The {@link TransitionStrategy}! It takes the Hosts, which should be moved
* M2: The {@link ITransitionStrategy}! It takes the Hosts, which should be moved
* around, but calculates only the assignment to the {@link AttractionPoint}s.
* It doesn't move the Hosts! It will be only assignment a new AttractionPoint!
*
......@@ -85,7 +85,12 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.LocationAc
* destination point (AttractionPoint), so that not all hosts, which are
* assigned to one {@link AttractionPoint}, lies on the same point.<br>
*
*
* 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
......@@ -106,10 +111,12 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
protected IMapVisualization mapVisualization;
private Set<SimLocationActuator> moveableHosts = new LinkedHashSet<SimLocationActuator>();
private ModularMovementModelViz modelVisualisation;
protected Set<SimLocationActuator> moveableHosts = new LinkedHashSet<SimLocationActuator>();
private Map<SimLocationActuator, PositionVector> currentTarget = new LinkedHashMap<>();
private boolean initialized = false;
private long timeBetweenMoveOperation = Simulator.SECOND_UNIT;
......@@ -121,6 +128,9 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
.getWorldDimensions();
this.rand = Randoms.getRandom(ModularMovementModel.class);
// Setting this model as default to prevent older code from braking. See Changelog.
modelVisualisation = new ModularMovementModelViz(this);
// scheduling initalization!
Event.scheduleImmediately(this, null, EVENT_INIT);
}
......@@ -132,8 +142,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
public void initialize() {
if (!initialized) {
VisualizationInjector
.injectComponent(new ModularMovementModelViz(this));
VisualizationInjector.injectComponent(modelVisualisation);
if (mapVisualization != null) {
VisualizationInjector.injectComponent(mapVisualization);
}
......@@ -230,7 +239,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
"time is negative for the Move Operations");
}
}
@Override
public void updatedAttractionAssignment(SimLocationActuator component,
AttractionPoint newAssignment) {
......@@ -244,7 +253,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
* Even if an AP does not have a radius, we slightly offset
*/
double apRadius = Math.max(newAssignment.getRadius(), 25.0);
int tries = 0;
do {
destination = new PositionVector(attractionCenter);
......@@ -268,7 +277,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
}
tries++;
} while (destination == null);
currentTarget.put(component, destination);
}
......@@ -290,7 +299,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
* @param ms
* @param destination
*/
private void doLocalMovement(SimLocationActuator ms,
protected void doLocalMovement(SimLocationActuator ms,
PositionVector destination) {
Either<PositionVector, Boolean> either = localMovementStrategy
......@@ -331,6 +340,12 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
this.mapVisualization = mapVisualization;
}
public void setModelVisualisation(ModularMovementModelViz modelVis)
{
modelVisualisation = modelVis;
modelVisualisation.setMovementModel(this);
}
@Override
public void eventOccurred(Object content, int type) {
if (type == EVENT_INIT) {
......
......@@ -52,7 +52,7 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Attraction
public class ModularMovementModelViz extends JComponent
implements VisualizationComponent {
private ModularMovementModel movementModel;
protected ModularMovementModel movementModel;
protected boolean showAttractionPoints = true;
......@@ -66,16 +66,26 @@ public class ModularMovementModelViz extends JComponent
private static Color COLOR_ATTR_POINT = Color.decode("#4A7B9D");
public ModularMovementModelViz()
{
init();
}
protected void init()
{
setBounds(0, 0, VisualizationInjector.getWorldX(),
VisualizationInjector.getWorldY());
setOpaque(true);
setVisible(true);
menu = new JMenu("Mobility Model");
menu.add(createCheckboxAp());
menu.add(createCheckboxNodePositions());
}
public ModularMovementModelViz(ModularMovementModel model) {
setBounds(0, 0, VisualizationInjector.getWorldX(),
VisualizationInjector.getWorldY());
setOpaque(true);
setVisible(true);
init();
this.movementModel = model;
menu = new JMenu("Mobility Model");
menu.add(createCheckboxAp());
menu.add(createCheckboxNodePositions());
}
private JCheckBoxMenuItem createCheckboxAp() {
......@@ -112,43 +122,79 @@ public class ModularMovementModelViz extends JComponent
RenderingHints.VALUE_ANTIALIAS_ON);
if (showAttractionPoints) {
for (AttractionPoint aPoint : movementModel.getAttractionPoints()) {
Point point = ((PositionVector) aPoint).asPoint();
// draw border
g2.setColor(Color.BLACK);
g2.setFont(VisualizationTopologyView.FONT_MEDIUM);
g2.drawString(aPoint.getName(),
VisualizationInjector.scaleValue(point.x) - ATTR_PAD,
VisualizationInjector.scaleValue(point.y) - ATTR_PAD);
g2.setColor(COLOR_ATTR_POINT);
float alpha = 0.25f + (float) (aPoint.getWeight() / 2);
g2.setComposite(AlphaComposite
.getInstance(AlphaComposite.SRC_OVER, alpha));
g2.fillOval(
VisualizationInjector.scaleValue(point.x) - ATTR_PAD,
VisualizationInjector.scaleValue(point.y) - ATTR_PAD,
ATTR_PAD * 2 + 1, ATTR_PAD * 2 + 1);
g2.setColor(COLOR_ATTR_POINT);
int radius = (int) aPoint.getRadius() + ATTR_PAD;
g2.drawOval(VisualizationInjector.scaleValue(point.x) - radius,
VisualizationInjector.scaleValue(point.y) - radius,
radius * 2 + 1, radius * 2 + 1);
}
drawAttractionPoints(g2);
}
if (showNodePositions) {
for (SimLocationActuator comp : movementModel
.getAllLocationActuators()) {
Point2D pt = comp.getRealPosition().asPoint();
g2.setColor(Color.GRAY);
g2.fillOval((int) pt.getX() - NODE_PAD,
(int) pt.getY() - NODE_PAD, NODE_PAD * 2 + 1,
NODE_PAD * 2 + 1);
}
for (SimLocationActuator comp : movementModel
.getAllLocationActuators()) {
drawNodePosition(g2, comp);
}
}
}
/**
* Provides access super.paintComponent() for extending classes.
* @param g the Graphics object for painting.
*/
protected void paintSuper(Graphics g)
{
super.paintComponent(g);
}
/**
* Draws the attraction points. This method has been extracted from paint()
* to make it possible for extending class to override only this bit while leaving everything
* else untouched.
* @param g2 the Graphics2D object for painting.
*/
protected void drawAttractionPoints(Graphics2D g2)
{
for (AttractionPoint aPoint : movementModel.getAttractionPoints()) {
Point point = ((PositionVector) aPoint).asPoint();
// draw border
g2.setColor(Color.BLACK);
g2.setFont(VisualizationTopologyView.FONT_MEDIUM);
g2.drawString(aPoint.getName(),
VisualizationInjector.scaleValue(point.x) - ATTR_PAD,
VisualizationInjector.scaleValue(point.y) - ATTR_PAD);
g2.setColor(COLOR_ATTR_POINT);
float alpha = 0.25f + (float) (aPoint.getWeight() / 2);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
g2.fillOval(
VisualizationInjector.scaleValue(point.x) - ATTR_PAD,
VisualizationInjector.scaleValue(point.y) - ATTR_PAD,
ATTR_PAD * 2 + 1, ATTR_PAD * 2 + 1);
g2.setColor(COLOR_ATTR_POINT);
int radius = (int) aPoint.getRadius() + ATTR_PAD;
g2.drawOval(VisualizationInjector.scaleValue(point.x) - radius,
VisualizationInjector.scaleValue(point.y) - radius,
radius * 2 + 1, radius * 2 + 1);
}
}
/**
* Draws the position of the node. This method has been extracted from paint()
* to make it possible for extending class to override only this bit while leaving everything
* else untouched. Instead of painting all of the nodes, only a specific one gets painted. This allows
* extending class to fall back to this implementation if one specific node could not be drawn for whatever reason.
* @param g2 the Graphics2D object for painting.
*/
protected void drawNodePosition(Graphics2D g2, SimLocationActuator comp)
{
Point2D pt = comp.getRealPosition().asPoint();
g2.setColor(Color.GRAY);
g2.fillOval(VisualizationInjector.scaleValue(pt.getX()) - NODE_PAD,
VisualizationInjector.scaleValue(pt.getY()) - NODE_PAD, NODE_PAD * 2 + 1,
NODE_PAD * 2 + 1);
}
protected void setMovementModel(ModularMovementModel model)
{
movementModel = model;
}
@Override
public String getDisplayName() {
return "Mobility Model";
......
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.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;
/**
* 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. <BR><BR>
*
* 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.<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,
* 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<SimLocationActuator, String> movementTypes;
private HashMap<SimLocationActuator, ITransitionStrategy> transitions;
private HashMap<Class, ITransitionStrategy> supportedTransitions;
private LinkedList<MultiTypeMovementListener> 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<PositionVector, Boolean> 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);
}
/**
* 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)
{
ITransitionStrategy selectedStrategy = supportedTransitions.get(strategy);
if(selectedStrategy == null)
{
throw new UnsupportedOperationException(
String.format("ModularMultiTypeMovementModel: TransitionStrategy %s ist not supported!", strategy.toString()));
}
changeTransitionStrategy(ms, selectedStrategy);
}
/**
* 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()));
}
/**
* 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);
}
}
package de.tud.kom.p2psim.impl.topology.movement.modularosm;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
/**
* Created by Clemens on 13.03.2017.
*/
public interface MultiTypeMovementListener
{
/**
* Gets called when the {@link SimLocationActuator} can't move
* any closer to the destination. This does NOT necessarily mean that he reached the
* destination!
*/
void onTransition(SimLocationActuator ms);
}
package de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint;
/**
* A basic attraction point, as simple as it can get. Really just a named location
* without any checks performed.
*/
public class BasicAttractionPoint extends PositionVector implements AttractionPoint
{
private String name;
private double weight = 0;
private double radius = 0;
public BasicAttractionPoint(String name, PositionVector pos)
{
super(pos);
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public double getWeight() {
return weight;
}
@Override
public double getRadius() {
return radius;
}
@Override
public void setWeight(double weight) {
this.weight = weight;
}
@Override
public void setRadius(double radius) {
this.radius = radius;
}
@Override
public AttractionPoint clone(String newName) {
return new BasicAttractionPoint(newName, this);
}
}
......@@ -38,8 +38,12 @@ import java.net.URL;
import javax.swing.JComponent;
import javax.swing.JMenu;
import de.tud.kom.p2psim.api.topology.Topology;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.GPSCalculation;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException;
public class ShowMapQuestMapViz extends JComponent
implements IMapVisualization {
......@@ -66,25 +70,33 @@ public class ShowMapQuestMapViz extends JComponent
private void initializeImage() {
if (!initialized) {
PositionVector worldDimensions = Binder.getComponentOrNull(Topology.class)
.getWorldDimensions();
tempImageFilePath = tempImageFilePath + "mapquest"
+ GPSCalculation.getLatCenter()
+ GPSCalculation.getLonCenter() + GPSCalculation.getZoom()
+ VisualizationInjector.getWorldX()
+ VisualizationInjector.getWorldY() + mapType + ".jpg";
+ GPSCalculation.getLatCenter() + "_"
+ GPSCalculation.getLonCenter() + "_" + GPSCalculation.getZoom() + "_"
+ worldDimensions.getX() + "_"
+ worldDimensions.getY() + mapType + ".jpg";
// Check if the file with same properties (same location) already
// exists
File f = new File(tempImageFilePath);
if (!f.exists()) {
try {
//Based on the meters per pixel, the needed height and width in pixels can be determined.
int pxx = (int) (worldDimensions.getX() / GPSCalculation.getMetersPerPixel());
int pxy = (int) (worldDimensions.getY() / GPSCalculation.getMetersPerPixel());
String imageUrl = "http://www.mapquestapi.com/staticmap/v4/getmap?key="
+ mapQuestKey + "&type=" + mapType
+ "&imagetype=jpeg&center="
+ GPSCalculation.getLatCenter() + ","
+ GPSCalculation.getLonCenter() + "&zoom="
+ ((GPSCalculation.getZoom()) + 1) + "&size="
+ VisualizationInjector.getWorldX() + ","
+ VisualizationInjector.getWorldY();
+ ((GPSCalculation.getZoom())) + "&size="
+ pxx + ","
+ pxy;
URL url = new URL(imageUrl);
InputStream is = url.openStream();
OutputStream os = new FileOutputStream(tempImageFilePath);
......
package de.tud.kom.p2psim.impl.topology.movement.modularosm.movementIconVisualisation;
/**
* Mapping of objects to icons, for usage with the {@link ModularMovementIconVis}, especially
* if the config files.
*
* @author Clemens Krug
*/
public class IconMapping
{
private String object;
private String iconpath;
public void setObject(String icon)
{
object = icon;
}
public void setIconPath(String path)
{
iconpath = path;
}
public String getObject() {
return object;
}
public String getIconpath() {
return iconpath;
}
}
package de.tud.kom.p2psim.impl.topology.movement.modularosm.movementIconVisualisation;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.ModularMovementModelViz;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView;
import de.tudarmstadt.maki.simonstrator.api.*;
import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Point2D;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Visualises the different nodes as icons instead of these lame dots.
* The icons are configured entirely in the config files. To use this visualisation
* 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
{
private HashMap<Class, String> pathMap;
private HashMap<Class, Image> classIconMap;
private HashMap<String, String> stateMap;
private HashMap<String, Image> stateIconMap;
private StateSelector stateSelector;
private boolean init = false;
public ModularMovementIconVis()
{
init();
pathMap = new HashMap<>();
classIconMap = new HashMap<>();
stateMap = new HashMap<>();
stateIconMap = new HashMap<>();
// scheduling initalization!
de.tudarmstadt.maki.simonstrator.api.Event.scheduleImmediately(this, null, 0);
}
private void initialize()
{
assert !init : "ModularMovementIconVis: Was already initialized!";
VisualizationTopologyView.VisualizationInjector.getTopologyView().setShowNodes(false);
for (Map.Entry<Class, String> e : pathMap.entrySet())
{
try
{
Image icon = ImageIO.read(new File(e.getValue())).getScaledInstance(-1, 15, Image.SCALE_FAST);
classIconMap.put(e.getKey(), icon);
} 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())
{
assert stateSelector != null : "There must be a state selector specified when using state icons!";
try
{
Image icon = ImageIO.read(new File(e.getValue())).getScaledInstance(-1, 15, Image.SCALE_FAST);
stateIconMap.put(e.getKey(), icon);
} catch (IOException ioe)
{
Monitor.log(ModularMovementIconVis.class, Monitor.Level.WARN, "Could not load icon from path %s", e.getValue());
}
}
init = true;
}
@Override
public void paint(Graphics g)
{
super.paintSuper(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (showAttractionPoints)
{
super.drawAttractionPoints(g2);
}
if (showNodePositions)
{
for (SimLocationActuator comp : movementModel
.getAllLocationActuators())
{
drawNodePosition(g2, comp);
}
}
}
@Override
protected void drawNodePosition(Graphics2D g2, SimLocationActuator comp)
{
boolean iconNotFound = true;
Host host = comp.getHost();
Point2D pt = comp.getRealPosition().asPoint();
pt = new Point2D.Double(VisualizationTopologyView.VisualizationInjector.scaleValue(pt.getX()), VisualizationTopologyView.VisualizationInjector.scaleValue(pt.getY()));
g2.setColor(Color.GRAY);
// 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))
{
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())
{
try
{
host.getComponent(e.getKey());
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)
{
iconNotFound = true;
}
}
// If not, use standard visualisation
if (iconNotFound) super.drawNodePosition(g2, comp);
}
@Override
public void eventOccurred(Object content, int type)
{
if (type == 0)
{
initialize();
}
}
public void setClassIcon(IconMapping classIcon)
{
try
{
pathMap.put(Class.forName(classIcon.getObject()), classIcon.getIconpath());
} catch (ClassNotFoundException e)
{
Monitor.log(ModularMovementIconVis.class, Monitor.Level.WARN, "Class %s not found!", classIcon.getObject());
}
}
public void setStateIcon(IconMapping stateIcon)
{
stateMap.put(stateIcon.getObject(), stateIcon.getIconpath());
}
public void setStateSelector(StateSelector selector)
{
this.stateSelector = selector;
}
}
package de.tud.kom.p2psim.impl.topology.movement.modularosm.movementIconVisualisation;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
/**
* Interface for the state selector which shall be used in conjunction with the
* {@link ModularMovementIconVis} in order to use state icons.
*
* @author Clemens Krug
*/
public interface StateSelector
{
/**
* Returns the state of the host as string. The returned state
* must be equal to the one specified in the state-icon mapping to have any effect.
* The state of a host can not be null! In case there is no state matched which has an icon
* specified, you can return whatever string you like.
*
* @param ms The host of whom the state should be determined.
* @return The state of the host as string, not null.
*/
String getState(SimLocationActuator ms);
}
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