Commit 2e28bc46 authored by Björn Richerzhagen's avatar Björn Richerzhagen
Browse files

Merge branch 'master' into tm/sumo-integration

Conflicts:
	src/de/tud/kom/p2psim/impl/topology/DefaultTopologyComponent.java
parents 8aeec4ee 686eca40
...@@ -340,7 +340,7 @@ ...@@ -340,7 +340,7 @@
<dependency> <dependency>
<groupId>com.graphhopper</groupId> <groupId>com.graphhopper</groupId>
<artifactId>graphhopper</artifactId> <artifactId>graphhopper</artifactId>
<version>0.5.0</version> <version>0.7.0</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<groupId>org.slf4j</groupId> <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 @@ ...@@ -20,7 +20,6 @@
package de.tud.kom.p2psim.api.application; 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. * 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; ...@@ -36,13 +35,20 @@ import de.tud.kom.p2psim.api.common.SimHost;
* *
* @author Christoph Muenker * @author Christoph Muenker
* @version 1.0, 14.06.2013 * @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 * Called when a {@link WorkloadItem} is ready.
* * @param item The workload item.
* @return The application which is using the listener.
*/ */
public Application getApplication(); void doWork(WorkloadItem item);
} }
package de.tud.kom.p2psim.api.common;
/**
* Created by Clemens on 02.04.2017.
*/
public interface NodeDebugUpdateListener
{
}
...@@ -19,73 +19,61 @@ ...@@ -19,73 +19,61 @@
*/ */
package de.tud.kom.p2psim.api.transport; package de.tud.kom.p2psim.api.transport;
/** /**
* Instances of this type describe the service properties chosen for message * Instances of this type describe the service properties chosen for message
* transmission. * transmission.
* *
* @author Sebastian Kaune * @author Sebastian Kaune
* @author Konstantin Pussep * @author Konstantin Pussep
* @version 3.0, 11/29/2007 * @version 3.0, 11/29/2007
* *
*/ */
public enum TransProtocol { public enum TransProtocol {
/**
* User Datagram Protocol
*/
UDP(false, false, 8),
/**
* Transmission Control Protocol
*/
TCP(true, true, 20),
/** /**
* Mobile Communication, one hop (this will be changed to UDP/TCP on the * User Datagram Protocol
* BaseStation or one of its backbone nodes). Only available, if an
* UMTS-transceiver is configured in the LinkLayer. TODO check Header Size
*/ */
UMTS(true, true, 20), UDP(false, false, 8),
/** /**
* Bluetooth (this will only allow one-hop communication to short-ranged * Transmission Control Protocol
* neighbors). Only available, if a BT-transceiver is configured in the
* LinkLayer. TODO check Header Size
*/ */
BLUETOOTH(true, true, 20); TCP(true, true, 20);
private boolean isConOriented; private boolean isConOriented;
private boolean isReliable; private boolean isReliable;
private int headerSize; private int headerSize;
private TransProtocol(boolean isConOriented, boolean isReliable, private TransProtocol(boolean isConOriented, boolean isReliable,
int headerSize) { int headerSize) {
this.isConOriented = isConOriented; this.isConOriented = isConOriented;
this.isReliable = isReliable; this.isReliable = isReliable;
this.headerSize = headerSize; this.headerSize = headerSize;
} }
/** /**
* If the used service is connection-oriented the in-order delivery of * If the used service is connection-oriented the in-order delivery of
* messages is guaranteed. * messages is guaranteed.
* *
* @return whether the applied service should be connection-oriented * @return whether the applied service should be connection-oriented
*/ */
public boolean isConnectionOriented() { public boolean isConnectionOriented() {
return this.isConOriented; return this.isConOriented;
} }
/** /**
* Whether the used connection is reliable, i.e. the network wrapper tries * Whether the used connection is reliable, i.e. the network wrapper tries
* to deliver the message in case of loss or and if the message cannot be * to deliver the message in case of loss or and if the message cannot be
* delivered an exception is reported to the upper layer. * delivered an exception is reported to the upper layer.
* *
* @return whether the applied service should be reliable * @return whether the applied service should be reliable
*/ */
public boolean isReliable() { public boolean isReliable() {
return this.isReliable; return this.isReliable;
} }
/** /**
* Size of the headers of this TransMessage in byte. * Size of the headers of this TransMessage in byte.
...@@ -95,5 +83,5 @@ public enum TransProtocol { ...@@ -95,5 +83,5 @@ public enum TransProtocol {
public int getHeaderSize() { public int getHeaderSize() {
return this.headerSize; return this.headerSize;
} }
} }
...@@ -99,7 +99,7 @@ public class MaxPeerCountChurnGenerator ...@@ -99,7 +99,7 @@ public class MaxPeerCountChurnGenerator
private PriorityQueue<HostSessionInfo> offlineHostsSortedByOfflineTime; private PriorityQueue<HostSessionInfo> offlineHostsSortedByOfflineTime;
private Random rnd = Randoms.getRandom(new Object()); private Random rnd = Randoms.getRandom(MaxPeerCountChurnGenerator.class);
/** /**
* Comparator used to sort client infos by offline time * Comparator used to sort client infos by offline time
......
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 { ...@@ -737,7 +737,7 @@ public class DefaultConfigurator implements Configurator {
if (cArgs.length != types.length) if (cArgs.length != types.length)
throw new ConfigurationException( throw new ConfigurationException(
"The size of the argument list of the XML configurable constructor (" "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."); + ") is unequal to the size of arguments of the constructor is was applied to.");
// Constructor can be called with the given XML // Constructor can be called with the given XML
......
...@@ -32,6 +32,7 @@ import de.tud.kom.p2psim.api.topology.obstacles.ObstacleModel; ...@@ -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.social.SocialView;
import de.tud.kom.p2psim.api.topology.views.TopologyView; import de.tud.kom.p2psim.api.topology.views.TopologyView;
import de.tud.kom.p2psim.api.topology.waypoints.WaypointModel; 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.VisualizationTopologyView.VisualizationInjector;
import de.tud.kom.p2psim.impl.topology.views.visualization.world.SocialViewComponentVis; import de.tud.kom.p2psim.impl.topology.views.visualization.world.SocialViewComponentVis;
...@@ -63,6 +64,8 @@ public class DefaultTopology implements Topology { ...@@ -63,6 +64,8 @@ public class DefaultTopology implements Topology {
public DefaultTopology(PositionVector worldDimensions) { public DefaultTopology(PositionVector worldDimensions) {
this.worldDimensions = worldDimensions; this.worldDimensions = worldDimensions;
components = new LinkedList<TopologyComponent>(); components = new LinkedList<TopologyComponent>();
// obstacles = new LinkedList<Obstacle>(); // obstacles = new LinkedList<Obstacle>();
topoListeners = new LinkedList<TopologyListener>(); topoListeners = new LinkedList<TopologyListener>();
......
...@@ -90,6 +90,8 @@ public class DefaultTopologyComponent implements TopologyComponent { ...@@ -90,6 +90,8 @@ public class DefaultTopologyComponent implements TopologyComponent {
private MovementModel movementModel; private MovementModel movementModel;
private PlacementModel placementModel; private PlacementModel placementModel;
private final boolean registerAsInformationProviderInSiS;
/** /**
* Create a TopologyComponent for the current host. * Create a TopologyComponent for the current host.
...@@ -99,7 +101,7 @@ public class DefaultTopologyComponent implements TopologyComponent { ...@@ -99,7 +101,7 @@ public class DefaultTopologyComponent implements TopologyComponent {
* @param movementModel * @param movementModel
*/ */
public DefaultTopologyComponent(SimHost host, Topology topology, public DefaultTopologyComponent(SimHost host, Topology topology,
MovementModel movementModel, PlacementModel placementModel) { MovementModel movementModel, PlacementModel placementModel, boolean registerAsInformationProviderInSiS) {
this.topology = topology; this.topology = topology;
this.host = host; this.host = host;
this.position = new PositionVector(0, 0); this.position = new PositionVector(0, 0);
...@@ -113,6 +115,7 @@ public class DefaultTopologyComponent implements TopologyComponent { ...@@ -113,6 +115,7 @@ public class DefaultTopologyComponent implements TopologyComponent {
if (this.placementModel != null) { if (this.placementModel != null) {
this.placementModel.addComponent(this); this.placementModel.addComponent(this);
} }
this.registerAsInformationProviderInSiS = registerAsInformationProviderInSiS;
} }
@Override @Override
...@@ -131,80 +134,83 @@ public class DefaultTopologyComponent implements TopologyComponent { ...@@ -131,80 +134,83 @@ public class DefaultTopologyComponent implements TopologyComponent {
position.set(placementModel.place(this)); position.set(placementModel.place(this));
} }
try { if (registerAsInformationProviderInSiS) {
final SiSComponent sis = host.getComponent(SiSComponent.class); try {
sis.provide().nodeState(SiSTypes.PHY_LOCATION, final SiSComponent sis = host.getComponent(SiSComponent.class);
new SiSDataCallback<Location>() { sis.provide().nodeState(SiSTypes.PHY_LOCATION,
new SiSDataCallback<Location>() {
Set<INodeID> localID = INodeID
.getSingleIDSet(getHost().getId()); Set<INodeID> localID = INodeID
.getSingleIDSet(getHost().getId());
@Override
public Location getValue(INodeID nodeID, @Override
SiSProviderHandle providerHandle) public Location getValue(INodeID nodeID,
throws InformationNotAvailableException { SiSProviderHandle providerHandle)
if (nodeID.equals(getHost().getId())) { throws InformationNotAvailableException {
return getLastLocation(); if (nodeID.equals(getHost().getId())) {
} else { return getLastLocation();
throw new InformationNotAvailableException(); } else {
} throw new InformationNotAvailableException();
} }
}
@Override @Override
public Set<INodeID> getObservedNodes() { public Set<INodeID> getObservedNodes() {
return localID; return localID;
} }
@Override @Override
public SiSInfoProperties getInfoProperties() { public SiSInfoProperties getInfoProperties() {
return new SiSInfoProperties(); return new SiSInfoProperties();
} }
}); });
sis.provide().nodeState(SiSTypes.SPEED, sis.provide().nodeState(SiSTypes.SPEED,
new SiSDataCallback<Double>() { new SiSDataCallback<Double>() {
Set<INodeID> localID = INodeID Set<INodeID> localID = INodeID
.getSingleIDSet(getHost().getId()); .getSingleIDSet(getHost().getId());
@Override @Override
public Double getValue(INodeID nodeID, public Double getValue(INodeID nodeID,
SiSProviderHandle providerHandle) SiSProviderHandle providerHandle)
throws InformationNotAvailableException { throws InformationNotAvailableException {
if (nodeID.equals(getHost().getId())) { if (nodeID.equals(getHost().getId())) {
return getMovementSpeed(); return getMovementSpeed();
} else { } else {
throw new InformationNotAvailableException(); throw new InformationNotAvailableException();
} }
} }
@Override @Override
public Set<INodeID> getObservedNodes() { public Set<INodeID> getObservedNodes() {
return localID; return localID;
} }
@Override @Override
public SiSInfoProperties getInfoProperties() { public SiSInfoProperties getInfoProperties() {
return new SiSInfoProperties(); return new SiSInfoProperties();
} }
}); });
// Provide Underlay topology // Provide Underlay topology
Event.scheduleImmediately(new EventHandler() { Event.scheduleImmediately(new EventHandler() {
@Override @Override
public void eventOccurred(Object content, int type) { public void eventOccurred(Object content, int type) {
if (getHost().getLinkLayer().hasPhy(PhyType.WIFI)) { if (getHost().getLinkLayer().hasPhy(PhyType.WIFI)) {
new SiSTopologyProvider(sis, SiSTypes.NEIGHBORS_WIFI, new SiSTopologyProvider(sis,
DefaultTopologyComponent.this, SiSTypes.NEIGHBORS_WIFI,
getTopologyID(NetInterfaceName.WIFI, true), DefaultTopologyComponent.this,
DefaultTopologyComponent.class); getTopologyID(NetInterfaceName.WIFI, true),
DefaultTopologyComponent.class);
}
} }
} }, null, 0);
}, null, 0);
} catch (ComponentNotAvailableException e) { } catch (ComponentNotAvailableException e) {
// OK // OK
}
} }
} }
...@@ -285,6 +291,18 @@ public class DefaultTopologyComponent implements TopologyComponent { ...@@ -285,6 +291,18 @@ public class DefaultTopologyComponent implements TopologyComponent {
@Override @Override
public void updateCurrentLocation(Location location) { public void updateCurrentLocation(Location location) {
position.set(location); position.set(location);
/*
* FIXME utilization of the NodeDebugMonitor leads to huge performance drop.
*/
// NodeDebugMonitor.update(this.getClass(), getHost().getId(), "Current Location", location);
// try {
// NodeDebugMonitor.update(this.getClass(), getHost().getId(),
// "Distance to target",
// location.distanceTo(movementModel.getTargetLocation(this)));
// } catch (UnsupportedOperationException e) {
// // This is not supported by the movement model (which may happen see
// // MovementModel.java - thus catch and ignore)
// }
// notify "non-request" listeners // notify "non-request" listeners
for (LocationListener locationListener : listeners) { for (LocationListener locationListener : listeners) {
locationListener.onLocationChanged(getHost(), getLastLocation()); locationListener.onLocationChanged(getHost(), getLastLocation());
...@@ -295,6 +313,7 @@ public class DefaultTopologyComponent implements TopologyComponent { ...@@ -295,6 +313,7 @@ public class DefaultTopologyComponent implements TopologyComponent {
public void setTargetAttractionPoint(AttractionPoint targetAttractionPoint) public void setTargetAttractionPoint(AttractionPoint targetAttractionPoint)
throws UnsupportedOperationException { throws UnsupportedOperationException {
movementModel.changeTargetLocation(this, targetAttractionPoint); movementModel.changeTargetLocation(this, targetAttractionPoint);
// NodeDebugMonitor.update(this.getClass(), getHost().getId(), "Target Location", targetAttractionPoint);
} }
@Override @Override
......
...@@ -23,8 +23,6 @@ package de.tud.kom.p2psim.impl.topology; ...@@ -23,8 +23,6 @@ package de.tud.kom.p2psim.impl.topology;
import java.awt.Point; import java.awt.Point;
import java.util.Arrays; import java.util.Arrays;
import org.aopalliance.aop.AspectException;
import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Coordinate;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location;
......
...@@ -71,6 +71,8 @@ public class TopologyFactory implements HostComponentFactory { ...@@ -71,6 +71,8 @@ public class TopologyFactory implements HostComponentFactory {
private WaypointModel waypointModel; private WaypointModel waypointModel;
private ObstacleModel obstacleModel; private ObstacleModel obstacleModel;
private boolean registerAsInformationProviderInSiS = false;
private static NetMeasurementDB measurementDB = null; private static NetMeasurementDB measurementDB = null;
...@@ -125,7 +127,7 @@ public class TopologyFactory implements HostComponentFactory { ...@@ -125,7 +127,7 @@ public class TopologyFactory implements HostComponentFactory {
* movement model. * movement model.
*/ */
TopologyComponent toCo = new DefaultTopologyComponent(host, topo, TopologyComponent toCo = new DefaultTopologyComponent(host, topo,
movement, placement); movement, placement, registerAsInformationProviderInSiS);
/* /*
* Need to register TopoViews as movement listeners, as they might need * Need to register TopoViews as movement listeners, as they might need
...@@ -227,6 +229,17 @@ public class TopologyFactory implements HostComponentFactory { ...@@ -227,6 +229,17 @@ public class TopologyFactory implements HostComponentFactory {
public void setUseRegionGroups(boolean useRegionGroups) { public void setUseRegionGroups(boolean useRegionGroups) {
TopologyFactory.useRegionGroups = useRegionGroups; TopologyFactory.useRegionGroups = useRegionGroups;
} }
/**
* Option to disable the default behavior of nodes registering as
* topology providers.
*
* @param registerAsLocationProviderInSiS
*/
public void setRegisterAsInformationProviderInSiS(
boolean registerAsInformationProviderInSiS) {
this.registerAsInformationProviderInSiS = registerAsInformationProviderInSiS;
}
/** /**
* Allows GNP-based strategies to retrieve the unique * Allows GNP-based strategies to retrieve the unique
......
...@@ -26,6 +26,7 @@ import de.tud.kom.p2psim.api.topology.obstacles.ObstacleModel; ...@@ -26,6 +26,7 @@ import de.tud.kom.p2psim.api.topology.obstacles.ObstacleModel;
import de.tud.kom.p2psim.api.topology.waypoints.WaypointModel; import de.tud.kom.p2psim.api.topology.waypoints.WaypointModel;
import de.tud.kom.p2psim.impl.topology.PositionVector; import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.util.Either; import de.tud.kom.p2psim.impl.util.Either;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.RouteSensor;
public abstract class AbstractLocalMovementStrategy implements public abstract class AbstractLocalMovementStrategy implements
LocalMovementStrategy { LocalMovementStrategy {
...@@ -34,6 +35,8 @@ public abstract class AbstractLocalMovementStrategy implements ...@@ -34,6 +35,8 @@ public abstract class AbstractLocalMovementStrategy implements
protected ObstacleModel obstacleModel; protected ObstacleModel obstacleModel;
private static boolean calculateRouteSegments = false;
private double scaleFactor = 1; private double scaleFactor = 1;
public double getMovementSpeed(SimLocationActuator ms) { public double getMovementSpeed(SimLocationActuator ms) {
...@@ -57,6 +60,37 @@ public abstract class AbstractLocalMovementStrategy implements ...@@ -57,6 +60,37 @@ public abstract class AbstractLocalMovementStrategy implements
this.obstacleModel = model; this.obstacleModel = model;
} }
protected boolean isCalculateRouteSegments() {
return calculateRouteSegments;
}
/**
* For the visualization: programmatically enable route segment
* calculations.
*/
public static void setCalculationOfRouteSegments(boolean setting) {
calculateRouteSegments = setting;
}
/**
* For the visualization: programmatically check route segment
* calculation.
*/
public static boolean isCalculatingRouteSegments() {
return calculateRouteSegments;
}
/**
* If you intend to use the {@link RouteSensor} and operate on a per-segment
* basis, set this to true. If you DO NOT use the respective segment data,
* set it to false, as this option consumes significant resources.
*
* @param calculateRouteSegments
*/
public void setCalculateRouteSegments(boolean calculateRouteSegments) {
this.calculateRouteSegments = calculateRouteSegments;
}
@Override @Override
public abstract Either<PositionVector, Boolean> nextPosition( public abstract Either<PositionVector, Boolean> nextPosition(
SimLocationActuator comp, PositionVector destination); SimLocationActuator comp, PositionVector destination);
......
...@@ -53,7 +53,7 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy { ...@@ -53,7 +53,7 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy {
private PositionVector worldDimensions; private PositionVector worldDimensions;
private static HashMap<Integer, RealWorldMovementPoints> movementPoints = new HashMap<>(); private static HashMap<SimLocationActuator, RouteImpl> movementPoints = new HashMap<>();
private String movementType; // {Fastest, Shortest, Pedestrian, Bicycle} private String movementType; // {Fastest, Shortest, Pedestrian, Bicycle}
private String mapQuestKey; private String mapQuestKey;
...@@ -82,8 +82,8 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy { ...@@ -82,8 +82,8 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy {
newPosition = destination.clone(); newPosition = destination.clone();
} else { } else {
//if not set already for this node or new destination is different than last one //if not set already for this node or new destination is different than last one
PointList pointList; RouteImpl trajectory = movementPoints.get(comp);
if(!movementPoints.containsKey(comp.hashCode()) || destination.distanceTo(movementPoints.get(comp.hashCode()).getDestination()) > 1.0) { if(trajectory == null || destination.distanceTo(trajectory.getDestination()) > 1.0) {
double[] startPosition = transformOwnWorldWindowToGPS(comp.getRealPosition().getX(), comp.getRealPosition().getY()); double[] startPosition = transformOwnWorldWindowToGPS(comp.getRealPosition().getX(), comp.getRealPosition().getY());
double[] destinationPosition = transformOwnWorldWindowToGPS(destination.getX(), destination.getY()); double[] destinationPosition = transformOwnWorldWindowToGPS(destination.getX(), destination.getY());
...@@ -110,7 +110,7 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy { ...@@ -110,7 +110,7 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy {
} }
//read in all waypoints //read in all waypoints
pointList = new PointList(); PointList pointList = new PointList();
if(allDirections != null) { if(allDirections != null) {
for(int i = 0; i < allDirections.length(); i++) { for(int i = 0; i < allDirections.length(); i++) {
try { try {
...@@ -123,25 +123,12 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy { ...@@ -123,25 +123,12 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy {
} }
} }
} }
movementPoints.put(comp.hashCode(), new RealWorldMovementPoints(comp.getRealPosition(), destination, pointList, 0)); trajectory = new RouteImpl(comp.getRealPosition(), destination, pointList);
} movementPoints.put(comp, trajectory);
else {
pointList = movementPoints.get(comp.hashCode()).getPointList();
} }
int actualIndex = movementPoints.get(comp.hashCode()).getActualIndex(); newPosition = trajectory.updateCurrentLocation(comp, getMovementSpeed(comp), 1.0);
int i = 0;
for(GHPoint3D temp : pointList) {
if(i==actualIndex) {
PositionVector nextPoint = transformGPSWindowToOwnWorld(temp.getLat(), temp.getLon());
newPosition = comp.getRealPosition().moveStep(nextPoint, getMovementSpeed(comp));
if(nextPoint.distanceTo(comp.getRealPosition()) < maxDistanceNextPoint) actualIndex++;
}
i++;
}
movementPoints.put(comp.hashCode(), new RealWorldMovementPoints(movementPoints.get(comp.hashCode()).getStart(), destination, pointList, actualIndex));
} }
return new Left<PositionVector, Boolean>(newPosition); return new Left<PositionVector, Boolean>(newPosition);
} }
...@@ -159,18 +146,6 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy { ...@@ -159,18 +146,6 @@ public class OnlineMapQuestMovement extends AbstractLocalMovementStrategy {
return gps_coordinates; return gps_coordinates;
} }
/**
* Projects the gps coordinates in the given gps window to the world-coordinates given in world-dimensions
* @param lat
* @param lon
* @return The projected position in world-dimensions
*/
private PositionVector transformGPSWindowToOwnWorld(double lat, double lon) {
double x = worldDimensions.getX() * (lon - lonLeft)/(lonRight - lonLeft);
double y = worldDimensions.getY() - worldDimensions.getY() * (lat - latLeft)/(latRight - latLeft);
return new PositionVector(x, y);
}
public void setMovementType(String movementType) { public void setMovementType(String movementType) {
this.movementType = movementType; this.movementType = movementType;
} }
......
...@@ -21,51 +21,72 @@ ...@@ -21,51 +21,72 @@
package de.tud.kom.p2psim.impl.topology.movement.local; package de.tud.kom.p2psim.impl.topology.movement.local;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import com.graphhopper.GHRequest; import com.graphhopper.GHRequest;
import com.graphhopper.GHResponse; import com.graphhopper.GHResponse;
import com.graphhopper.GraphHopper; import com.graphhopper.GraphHopper;
import com.graphhopper.PathWrapper;
import com.graphhopper.routing.util.EdgeFilter;
import com.graphhopper.routing.util.EncodingManager; import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.routing.util.TraversalMode;
import com.graphhopper.storage.index.LocationIndex;
import com.graphhopper.storage.index.QueryResult;
import com.graphhopper.util.DistanceCalc2D;
import com.graphhopper.util.EdgeIteratorState;
import com.graphhopper.util.Instruction;
import com.graphhopper.util.InstructionList;
import com.graphhopper.util.PointList; import com.graphhopper.util.PointList;
import com.graphhopper.util.shapes.GHPoint; 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.Topology;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator; import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.PositionVector; 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.movement.modularosm.GPSCalculation;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.RouteSensorComponent;
import de.tud.kom.p2psim.impl.util.Either; import de.tud.kom.p2psim.impl.util.Either;
import de.tud.kom.p2psim.impl.util.Left; import de.tud.kom.p2psim.impl.util.Left;
import de.tudarmstadt.maki.simonstrator.api.Binder; import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Monitor; import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Monitor.Level; import de.tudarmstadt.maki.simonstrator.api.Monitor.Level;
import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException;
/** /**
* This movement strategy uses the data from osm and navigates the nodes throught streets to the destination * 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 * @author Martin Hellwig
* @version 1.0, 07.07.2015 * @version 1.0, 07.07.2015
*/ */
public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
private PositionVector worldDimensions; private static PositionVector worldDimensions;
private GraphHopper hopper; private GraphHopper hopper;
private LocationIndex index;
private boolean init = false; private boolean init = false;
private static HashMap<SimLocationActuator, RealWorldMovementPoints> movementPoints = new HashMap<>(); private static HashMap<SimLocationActuator, RouteImpl> currentRoutes = new HashMap<>();
private Map<SimLocationActuator, RouteSensorComponent> routeSensorComponents = new LinkedHashMap<>();
private String osmFileLocation; //use pbf-format, because osm-format causes problems (xml-problems) private String osmFileLocation; //use pbf-format, because osm-format causes problems (xml-problems)
private String graphFolderFiles; private String graphFolderFiles;
private String movementType; //car, bike or foot private String movementType; //car, bike or foot
private String navigationalType; //fastest, private String defaultMovement;
private double latLeft; //Values from -90 to 90; always smaller than latRight private String navigationalType; //fastest,
private double latRight; //Values from -90 to 90 private static double latLower; //Values from -90 to 90; always smaller than latUpper
private double lonLeft; //Values from -180 to 180; Always smaller than lonRight private static double latUpper; //Values from -90 to 90
private double lonRight; //Values from -180 to 180 private static double lonLeft; //Values from -180 to 180; Always smaller than lonRight
private static double lonRight; //Values from -180 to 180
private boolean uniqueFolders; private boolean uniqueFolders;
/** /**
* Tolerance in meters (if the node reached a waypoint up to "tolerance" * Tolerance in meters (if the node reached a waypoint up to "tolerance"
* meters, it will select the next waypoint in the path. * meters, it will select the next waypoint in the path.
...@@ -73,17 +94,19 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { ...@@ -73,17 +94,19 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
private double tolerance = 1; private double tolerance = 1;
public RealWorldStreetsMovement() { public RealWorldStreetsMovement() {
this.worldDimensions = Binder.getComponentOrNull(Topology.class) worldDimensions = Binder.getComponentOrNull(Topology.class)
.getWorldDimensions(); .getWorldDimensions();
latLeft = GPSCalculation.getLatLower(); latLower = GPSCalculation.getLatLower();
latRight = GPSCalculation.getLatUpper(); latUpper = GPSCalculation.getLatUpper();
lonLeft = GPSCalculation.getLonLeft(); lonLeft = GPSCalculation.getLonLeft();
lonRight = GPSCalculation.getLonRight(); lonRight = GPSCalculation.getLonRight();
} }
private void init() { private void init() {
hopper = new GraphHopper().forServer(); hopper = new GraphHopper().forServer();
hopper.setOSMFile(osmFileLocation); hopper.setOSMFile(osmFileLocation);
// where to store graphhopper files? // where to store graphhopper files?
if (uniqueFolders) { if (uniqueFolders) {
Monitor.log(RealWorldStreetsMovement.class, Level.WARN, Monitor.log(RealWorldStreetsMovement.class, Level.WARN,
...@@ -97,19 +120,48 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { ...@@ -97,19 +120,48 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
} }
hopper.setEncodingManager(new EncodingManager(movementType)); hopper.setEncodingManager(new EncodingManager(movementType));
hopper.importOrLoad(); hopper.importOrLoad();
index = hopper.getLocationIndex();
init = true; init = true;
} }
@Override
public Either<PositionVector, Boolean> nextPosition(
SimLocationActuator comp, PositionVector destination) {
return nextPosition(comp, destination, defaultMovement);
}
public Either<PositionVector, Boolean> nextPosition(SimLocationActuator comp, public Either<PositionVector, Boolean> nextPosition(SimLocationActuator comp,
PositionVector destination) { PositionVector destination, String movementType) {
if(!init) init();
if (movementType == null || movementType.equals("")
|| !this.movementType.contains(movementType)) {
throw new AssertionError("Invalid movement type: " + movementType);
}
if (!init) {
init();
}
RouteSensorComponent routeSensor = routeSensorComponents.get(comp);
if (routeSensor == null) {
try {
routeSensorComponents.put(comp, comp.getHost()
.getComponent(RouteSensorComponent.class));
routeSensor = routeSensorComponents.get(comp);
} catch (ComponentNotAvailableException e) {
throw new AssertionError(
"The movement model needs to provide RouteSensorComponents.");
}
}
PositionVector newPosition = null; PositionVector newPosition = null;
if (destination if (destination
.distanceTo(comp.getRealPosition()) > getMovementSpeed(comp)) { .distanceTo(comp.getRealPosition()) > getMovementSpeed(comp)) {
//if not set already for this node or new destination is different than last one //if not set already for this node or new destination is different than last one
PointList pointList; RouteImpl trajectory = currentRoutes.get(comp);
if(!movementPoints.containsKey(comp) || destination.distanceTo(movementPoints.get(comp).getDestination()) > 1.0) { if(trajectory == null || destination.distanceTo(trajectory.getDestination()) > 1.0) {
double[] startPosition = transformOwnWorldWindowToGPS(comp.getRealPosition().getX(), comp.getRealPosition().getY()); double[] startPosition = transformOwnWorldWindowToGPS(comp.getRealPosition().getX(), comp.getRealPosition().getY());
double[] destinationPosition = transformOwnWorldWindowToGPS(destination.getX(), destination.getY()); double[] destinationPosition = transformOwnWorldWindowToGPS(destination.getX(), destination.getY());
GHRequest req = new GHRequest(startPosition[0], startPosition[1], destinationPosition[0], destinationPosition[1]). GHRequest req = new GHRequest(startPosition[0], startPosition[1], destinationPosition[0], destinationPosition[1]).
...@@ -119,51 +171,129 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { ...@@ -119,51 +171,129 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
GHResponse rsp = hopper.route(req); GHResponse rsp = hopper.route(req);
//If the requested point is not in the map data, simple return the destination as next point //If the requested point is not in the map data, simple return the destination as next point
if(rsp.hasErrors()) { if(rsp.hasErrors()) {
System.err.println("Requested Points (" + startPosition[0] + ", " + startPosition[1] Monitor.log(this.getClass(), Monitor.Level.ERROR, "Routing request for Host %s with starting point (" +
+ ") or (" + destinationPosition[0] + ", " + destinationPosition[1] + ") are out of the bounding box."); "%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 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)); trajectory = new RouteImpl(comp.getRealPosition(), destination, pointList);
} currentRoutes.put(comp, trajectory);
else {
pointList = rsp.getPoints(); } else {
movementPoints.put(comp, new RealWorldMovementPoints(comp.getRealPosition(), destination, pointList, 0)); PointList pointList = rsp.getBest().getPoints();
if (isCalculateRouteSegments()) {
/*
* Obtain segment IDs along the route.
*/
trajectory = new RouteImpl(comp.getRealPosition(),
destination, pointList,
routeSensor.getSegmentListeners(), routeSensor,
calculateSegments(rsp.getBest()));
} else {
trajectory = new RouteImpl(comp.getRealPosition(),
destination, pointList);
}
currentRoutes.put(comp, trajectory);
} }
routeSensor.setNewRoute(trajectory);
} }
else { newPosition = trajectory.updateCurrentLocation(comp, getMovementSpeed(comp), tolerance);
pointList = movementPoints.get(comp).getPointList();
if (trajectory.reachedDestination()) {
routeSensor.reachedDestination();
} }
}
int actualIndex = movementPoints.get(comp).getActualIndex(); return new Left<PositionVector, Boolean>(newPosition);
int i = 0; }
for(GHPoint3D temp : pointList) {
if(i==actualIndex) { /**
PositionVector nextPoint = transformGPSWindowToOwnWorld(temp.getLat(), temp.getLon()); * Calculate RouteSegments using the PathWrapper
newPosition = comp.getRealPosition().moveStep(nextPoint, getMovementSpeed(comp)); * @param pathWrapper
* @return
if (nextPoint */
.distanceTo(comp.getRealPosition()) < tolerance) { private List<RouteSegmentImpl> calculateSegments(PathWrapper pathWrapper) {
actualIndex++; InstructionList instructions = pathWrapper.getInstructions();
int ptIdx = 0;
List<RouteSegmentImpl> segments = new LinkedList<>();
RouteSegmentImpl lastSegment = null;
for (Instruction instr : instructions) {
RouteSegmentImpl segment = null;
PointList points = instr.getPoints();
String name = instr.getName();
if (name.isEmpty()) {
name = Integer.toString(instr.getPoints().toGHPoint(0).hashCode());
}
for (GHPoint point : points) {
if (segment == null) {
if (lastSegment != null) {
lastSegment.addRouteLocation(transformGPSWindowToOwnWorld(
point.getLat(), point.getLon()));
} }
segment = new RouteSegmentImpl(name, ptIdx, transformGPSWindowToOwnWorld(point.getLat(),
point.getLon()));
} else {
segment.addRouteLocation(transformGPSWindowToOwnWorld(
point.getLat(), point.getLon()));
} }
i++; ptIdx++;
} }
lastSegment = segment;
movementPoints.put(comp, new RealWorldMovementPoints(movementPoints.get(comp).getStart(), destination, pointList, actualIndex)); segments.add(segment);
} }
return new Left<PositionVector, Boolean>(newPosition);
return segments;
} }
/** /**
* Projects the world coordinates in the given gps window to the gps-coordinates * Calculate the route segments along the given point list.
*
* @return
*/
private List<RouteSegmentImpl> calculateSegments(PointList pointList) {
int ptIdx = 0;
int prevEdgeId = -1;
List<RouteSegmentImpl> segments = new LinkedList<>();
RouteSegmentImpl lastSegment = null;
for (GHPoint point : pointList) {
QueryResult qr = index.findClosest(point.getLat(), point.getLon(),
EdgeFilter.ALL_EDGES);
EdgeIteratorState nearEdge = qr.getClosestEdge();
if (nearEdge != null) {
int edgeId = nearEdge.getEdge();
if (edgeId != prevEdgeId) {
if (lastSegment != null) {
lastSegment.addRouteLocation(
transformGPSWindowToOwnWorld(point.getLat(),
point.getLon()));
}
lastSegment = new RouteSegmentImpl(Integer.toString(edgeId),
ptIdx, transformGPSWindowToOwnWorld(point.getLat(),
point.getLon()));
segments.add(lastSegment);
} else {
lastSegment.addRouteLocation(transformGPSWindowToOwnWorld(
point.getLat(), point.getLon()));
}
}
ptIdx++;
}
return segments;
}
/**
* Projects the world coordinates in the given gps window to the
* gps-coordinates
*
* @param x * @param x
* @param y * @param y
* @return The projected position in gps-coordinates (lat, long) * @return The projected position in gps-coordinates (lat, long)
*/ */
private double[] transformOwnWorldWindowToGPS(double x, double y) { private double[] transformOwnWorldWindowToGPS(double x, double y) {
double[] gps_coordinates = new double[2]; 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(); gps_coordinates[1] = lonLeft + (lonRight - lonLeft) * x/worldDimensions.getX();
return gps_coordinates; return gps_coordinates;
} }
...@@ -174,15 +304,68 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { ...@@ -174,15 +304,68 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
* @param lon * @param lon
* @return The projected position in world-dimensions * @return The projected position in world-dimensions
*/ */
private PositionVector transformGPSWindowToOwnWorld(double lat, double lon) { public static PositionVector transformGPSWindowToOwnWorld(double lat, double lon) {
double x = worldDimensions.getX() * (lon - lonLeft)/(lonRight - lonLeft); 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.max(0, x);
x = Math.min(worldDimensions.getX(), x); x = Math.min(worldDimensions.getX(), x);
y = Math.max(0, y); y = Math.max(0, y);
y = Math.min(worldDimensions.getY(), y); y = Math.min(worldDimensions.getY(), y);
return new PositionVector(x, y); return new PositionVector(x, y);
} }
/**
* 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<PositionVector> getMovementPoints(SimLocationActuator ms)
{
List<PositionVector> positions = new LinkedList<>();
PointList pointList = currentRoutes.get(ms).getPointList();
pointList.forEach(p -> positions.add(new PositionVector(transformGPSWindowToOwnWorld(p.getLat(), p.getLon()))));
return positions;
}
/**
* Calculates the length of a route in meters.
* @param start Starting position in own world coordinates (x / y)
* @param destination Destination on own world coordinates (x / y)
* @return the length of the route in meters.
*/
public double calculateRouteLength(PositionVector start, PositionVector destination)
{
PointList pointList;
double[] startPosition = transformOwnWorldWindowToGPS(start.getX(), start.getY());
double[] destinationPosition = transformOwnWorldWindowToGPS(destination.getX(), destination.getY());
GHRequest req = new GHRequest(startPosition[0], startPosition[1], destinationPosition[0], destinationPosition[1]).
setWeighting(navigationalType).
setVehicle(movementType).
setLocale(Locale.GERMANY);
GHResponse rsp = hopper.route(req);
//If the requested point is not in the map data, return -1
if(rsp.hasErrors()) {
return -1;
}
else {
pointList = rsp.getBest().getPoints();
return pointList.calcDistance(new DistanceCalc2D());
}
}
/**
* Calculates the length of the current route of the SimLocationActuator.
* @param ms the component
* @return the length of the current route
*/
public double getCurrentRouteLength(SimLocationActuator ms)
{
return currentRoutes.get(ms).getPointList().calcDistance(new DistanceCalc2D());
}
public void setOsmFileLocation(String osmFileLocation) { public void setOsmFileLocation(String osmFileLocation) {
this.osmFileLocation = osmFileLocation; this.osmFileLocation = osmFileLocation;
...@@ -194,6 +377,7 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { ...@@ -194,6 +377,7 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
public void setMovementType(String movementType) { public void setMovementType(String movementType) {
this.movementType = movementType; this.movementType = movementType;
defaultMovement = movementType.split(",")[0];
} }
public void setNavigationalType(String navigationalType) { public void setNavigationalType(String navigationalType) {
...@@ -203,13 +387,13 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy { ...@@ -203,13 +387,13 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
public void setWaypointTolerance(double tolerance) { public void setWaypointTolerance(double tolerance) {
this.tolerance = tolerance; this.tolerance = tolerance;
} }
/** /**
* For large batch simulations, we need to prevent same-time access to * For large batch simulations, we need to prevent same-time access to
* garphhopper temp data. Therefore, this flag creates unique folders for * garphhopper temp data. Therefore, this flag creates unique folders for
* each run (which, obviously, wastes a lot of space and comp-resources and * each run (which, obviously, wastes a lot of space and comp-resources and
* should not be used in standalone, single-threaded demo mode...) * should not be used in standalone, single-threaded demo mode...)
* *
* @param uniqueFolders * @param uniqueFolders
*/ */
public void setCreateUniqueFolders(boolean uniqueFolders) { public void setCreateUniqueFolders(boolean uniqueFolders) {
......
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.local;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import com.graphhopper.util.PointList;
import com.graphhopper.util.shapes.GHPoint3D;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.Route;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.RouteSensor;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.RouteSensor.RouteSegmentListener;
/**
* This class contains one trajectory from a start to a destination. It saves
* the respective points (coordinates) along the path with a given movement
* speed and also (if enabled) the assigned IDs of the roads and walkways that
* are traversed.
*
* TODO add segment handling
*
* Segment handling is only done if there is a segment listener registered for
* the respective client.
*
* @author Bjoern Richerzhagen
* @version 1.0, Aug 9, 2017
*/
public class RouteImpl implements Route {
private final RouteSensor sensor;
private PositionVector start;
private PositionVector destination;
private PointList pointList;
private final List<PositionVector> positionsList;
private final List<RouteSegmentImpl> routeSegments;
private final Set<RouteSegmentListener> segmentListeners;
private int currentIndex;
private int currentSegmentIndex;
private int routeLength;
private int routeSegmentsLength;
private boolean reachedTarget;
/**
*
* @param start
* @param destination
* @param pointList
* @param segmentListeners
* @param routeSegments
*/
public RouteImpl(PositionVector start,
PositionVector destination, PointList pointList,
Set<RouteSegmentListener> segmentListeners, RouteSensor sensor,
List<RouteSegmentImpl> routeSegments) {
this.sensor = sensor;
this.start = start;
this.destination = destination;
this.pointList = pointList;
this.positionsList = new LinkedList<>();
// Create Position vectors ONCE
for (GHPoint3D temp : pointList) {
positionsList
.add(RealWorldStreetsMovement.transformGPSWindowToOwnWorld(
temp.getLat(), temp.getLon()));
}
routeLength = positionsList.size();
this.segmentListeners = segmentListeners;
this.routeSegments = routeSegments;
this.routeSegmentsLength = routeSegments.size();
}
/**
*
* @param start
* @param destination
* @param pointList
*/
public RouteImpl(PositionVector start,
PositionVector destination, PointList pointList) {
this(start, destination, pointList, Collections.emptySet(), null,
Collections.emptyList());
}
public PositionVector getStart() {
return start;
}
public PositionVector getDestination() {
return destination;
}
@Override
public Location getTargetLocation() {
return destination;
}
@Override
public boolean hasSegmentInformation() {
return routeSegmentsLength != 0;
}
@Override
public RouteSegment getCurrentSegment() {
if (!hasSegmentInformation()) {
throw new UnsupportedOperationException(
"To access segments within a Route, you need to enable the respective functionality in the local movement strategy (usually: calculateRouteSegments=true)");
}
return routeSegments.get(currentSegmentIndex);
}
@Override
public List<RouteSegment> getSegmentsAhead() {
if (!hasSegmentInformation()) {
throw new UnsupportedOperationException(
"To access segments within a Route, you need to enable the respective functionality in the local movement strategy (usually: calculateRouteSegments=true)");
}
return Collections.unmodifiableList(routeSegments
.subList(currentSegmentIndex, routeSegmentsLength));
}
@Override
public List<RouteSegment> getSegmentsBehind() {
if (!hasSegmentInformation()) {
throw new UnsupportedOperationException(
"To access segments within a Route, you need to enable the respective functionality in the local movement strategy (usually: calculateRouteSegments=true)");
}
return Collections.unmodifiableList(routeSegments
.subList(0, currentSegmentIndex));
}
/**
* Update the internal location with the given movement speed
*
* @param speed
* @param realPosition
* @return
*/
public PositionVector updateCurrentLocation(SimLocationActuator comp,
double speed, double tolerance) {
List<PositionVector> sublist = positionsList.subList(currentIndex,
routeLength);
PositionVector newPosition = null;
for (PositionVector candidate : sublist) {
newPosition = comp.getRealPosition().moveStep(candidate, speed);
if (candidate.distanceTo(comp.getRealPosition()) < tolerance) {
currentIndex++;
} else {
break;
}
}
/*
* Segment handling (also inform listeners!)
*/
if (routeSegmentsLength > 0
&& routeSegmentsLength > currentSegmentIndex) {
List<RouteSegmentImpl> segmentSublist = routeSegments
.subList(currentSegmentIndex + 1, routeSegmentsLength);
for (RouteSegmentImpl candidate : segmentSublist) {
if (candidate.getFromRouteIndex() >= currentIndex) {
break;
}
currentSegmentIndex++;
segmentListeners.forEach(l -> l.onChangedRouteSegment(sensor,
this, routeSegments.get(currentSegmentIndex - 1),
candidate));
}
}
/*
* Reached target flag
*/
if (currentIndex >= routeLength) {
reachedTarget = true;
}
return newPosition;
}
/**
* True, if the trajectory is done and the node reached the target.s
*
* @return
*/
public boolean reachedDestination() {
return reachedTarget;
}
@Override
public String toString() {
return "\n\tfrom " + start.toString() + " to " + destination.toString()
+ "\n\tstep: " + currentIndex + " length: " + routeLength;
}
public PointList getPointList() {
return pointList;
}
public int getCurrentIndex() {
return this.currentIndex;
}
}
/*
* 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 <http://www.gnu.org/licenses/>.
*
*/
package de.tud.kom.p2psim.impl.topology.movement.local;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.Location;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.Route.RouteSegment;
/**
* Implementation of a {@link RouteSegment}
*
* @author Bjoern Richerzhagen
* @version 1.0, Aug 10, 2017
*/
public class RouteSegmentImpl implements RouteSegment {
private final String id;
private final int fromRouteIndex;
private final List<Location> routeLocations;
/**
* The segment and associated points (via their index)
*
* @param id
* @param fromRouteIndex
*/
public RouteSegmentImpl(String id, int fromRouteIndex,
Location startLocation) {
this.id = id;
this.fromRouteIndex = fromRouteIndex;
this.routeLocations = new LinkedList<>();
this.routeLocations.add(startLocation);
}
public void addRouteLocation(Location location) {
this.routeLocations.add(location);
}
@Override
public String getSegmentId() {
return id;
}
@Override
public List<Location> getSegmentLocations() {
return Collections.unmodifiableList(routeLocations);
}
public int getFromRouteIndex() {
return fromRouteIndex;
}
}
...@@ -25,7 +25,15 @@ import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.Visualiza ...@@ -25,7 +25,15 @@ import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.Visualiza
import de.tudarmstadt.maki.simonstrator.api.Binder; 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 * @author Martin Hellwig
* @version 1.0, Nov 3, 2015 * @version 1.0, Nov 3, 2015
*/ */
...@@ -37,7 +45,10 @@ public class GPSCalculation { ...@@ -37,7 +45,10 @@ public class GPSCalculation {
private static int zoom; private static int zoom;
private static double scaleFactor; private static double metersPerPixel;
// Earth Circumference in meters
private static int EARTH_CIRCUMFERENCE = 40030173;
/* /*
* http://gis.stackexchange.com/questions/2951/algorithm-for-offsetting-a- * http://gis.stackexchange.com/questions/2951/algorithm-for-offsetting-a-
...@@ -64,7 +75,8 @@ public class GPSCalculation { ...@@ -64,7 +75,8 @@ public class GPSCalculation {
* is valid on all zoom levels. * is valid on all zoom levels.
*/ */
// 17: 2, 16: 1, 15: 0.5, 14: 0.25 // 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() { public static double getLatCenter() {
...@@ -79,54 +91,58 @@ public class GPSCalculation { ...@@ -79,54 +91,58 @@ public class GPSCalculation {
return zoom; return zoom;
} }
public static double getLatUpper() { public static double getMetersPerPixel()
return latCenter + (Binder.getComponentOrNull(Topology.class) {
.getWorldDimensions().getY() / 111111d) * scaleFactor; return metersPerPixel;
}
public static double getWorldDimensionsX() {
return Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getX();
}
public static double getWorldDimensionsY() {
return Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getY();
}
// return latCenter + scaleFactor * 0.027613 * Binder public static double getLatUpper() {
// .getComponentOrNull(Topology.class).getWorldDimensions().getY() return latCenter + ((Binder.getComponentOrNull(Topology.class)
// / 1000; .getWorldDimensions().getY()/2) / 111111d);
} }
public static double getLatLower() { public static double getLatLower() {
return latCenter - (Binder.getComponentOrNull(Topology.class) return latCenter - ((Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getY() / 111111d) * scaleFactor; .getWorldDimensions().getY()/2) / 111111d);
// return latCenter - scaleFactor * 0.027613 * Binder
// .getComponentOrNull(Topology.class).getWorldDimensions().getY()
// / 1000;
} }
public static double getLonLeft() { public static double getLonLeft() {
return lonCenter - (Binder.getComponentOrNull(Topology.class) return lonCenter - ((Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getX() / 111111d / scaleLon) * scaleFactor; .getWorldDimensions().getX()/2) / (111111d * scaleLon));
// return lonCenter - scaleFactor * 0.043 * Binder
// .getComponentOrNull(Topology.class).getWorldDimensions().getX()
// / 1000;
} }
public static double getLonRight() { public static double getLonRight() {
return lonCenter + (Binder.getComponentOrNull(Topology.class) return lonCenter + ((Binder.getComponentOrNull(Topology.class)
.getWorldDimensions().getX() / 111111d / scaleLon) * scaleFactor; .getWorldDimensions().getX()/2) / (111111d * scaleLon));
// return lonCenter + scaleFactor * 0.043 * Binder
// .getComponentOrNull(Topology.class).getWorldDimensions().getX()
// / 1000;
} }
public void setLatCenter(double latCenter) { public void setLatCenter(double latCenter) {
this.latCenter = latCenter; this.latCenter = latCenter;
this.scaleLon = Math.cos(Math.toRadians(latCenter)); this.scaleLon = Math.cos(Math.toRadians(latCenter));
this.scaleFactor = 0.38d;
} }
public void setLonCenter(double lonCenter) { public void setLonCenter(double lonCenter) {
this.lonCenter = lonCenter; this.lonCenter = lonCenter;
} }
/**
* Formula http://wiki.openstreetmap.org/wiki/Zoom_levels
*
* @param zoom
*/
public void setZoom(int zoom) { 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(); setScaleFactor();
} }
} }
...@@ -24,13 +24,10 @@ import java.util.LinkedHashMap; ...@@ -24,13 +24,10 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.Vector; import java.util.Vector;
import javax.swing.JComponent;
import de.tud.kom.p2psim.api.scenario.ConfigurationException; import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.api.topology.Topology; 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.MovementModel;
...@@ -52,7 +49,6 @@ import de.tudarmstadt.maki.simonstrator.api.EventHandler; ...@@ -52,7 +49,6 @@ import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Randoms; import de.tudarmstadt.maki.simonstrator.api.Randoms;
import de.tudarmstadt.maki.simonstrator.api.Time; import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint; import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.LocationActuator;
/** /**
* Modular Movement Model uses different models/strategies to create a movement * Modular Movement Model uses different models/strategies to create a movement
...@@ -65,7 +61,7 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.LocationAc ...@@ -65,7 +61,7 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.LocationAc
* M1: A general {@link MovementModel} is not used, because we use static * M1: A general {@link MovementModel} is not used, because we use static
* attraction points. * attraction points.
* <p> * <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. * 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! * It doesn't move the Hosts! It will be only assignment a new AttractionPoint!
* *
...@@ -85,7 +81,12 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.LocationAc ...@@ -85,7 +81,12 @@ import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.LocationAc
* destination point (AttractionPoint), so that not all hosts, which are * destination point (AttractionPoint), so that not all hosts, which are
* assigned to one {@link AttractionPoint}, lies on the same point.<br> * 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 * @author Martin Hellwig, Christoph Muenker
* @version 1.0, 07.07.2015 * @version 1.0, 07.07.2015
...@@ -106,10 +107,14 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -106,10 +107,14 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
protected IMapVisualization mapVisualization; 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 Map<SimLocationActuator, PositionVector> currentTarget = new LinkedHashMap<>();
private Map<SimLocationActuator, RouteSensorComponent> routeSensorComponents = new LinkedHashMap<>();
private boolean initialized = false; private boolean initialized = false;
private long timeBetweenMoveOperation = Simulator.SECOND_UNIT; private long timeBetweenMoveOperation = Simulator.SECOND_UNIT;
...@@ -132,8 +137,10 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -132,8 +137,10 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
public void initialize() { public void initialize() {
if (!initialized) { if (!initialized) {
VisualizationInjector if (modelVisualisation == null) {
.injectComponent(new ModularMovementModelViz(this)); modelVisualisation = new ModularMovementModelViz(this);
}
VisualizationInjector.injectComponent(modelVisualisation);
if (mapVisualization != null) { if (mapVisualization != null) {
VisualizationInjector.injectComponent(mapVisualization); VisualizationInjector.injectComponent(mapVisualization);
} }
...@@ -215,8 +222,11 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -215,8 +222,11 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
@Override @Override
public void addComponent(SimLocationActuator comp) { public void addComponent(SimLocationActuator comp) {
moveableHosts.add(comp); moveableHosts.add(comp);
if (!routeSensorComponents.containsKey(comp)) {
routeSensorComponents.put(comp, new RouteSensorComponent(comp));
}
} }
public Set<SimLocationActuator> getAllLocationActuators() { public Set<SimLocationActuator> getAllLocationActuators() {
return moveableHosts; return moveableHosts;
} }
...@@ -230,7 +240,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -230,7 +240,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
"time is negative for the Move Operations"); "time is negative for the Move Operations");
} }
} }
@Override @Override
public void updatedAttractionAssignment(SimLocationActuator component, public void updatedAttractionAssignment(SimLocationActuator component,
AttractionPoint newAssignment) { AttractionPoint newAssignment) {
...@@ -244,7 +254,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -244,7 +254,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
* Even if an AP does not have a radius, we slightly offset * Even if an AP does not have a radius, we slightly offset
*/ */
double apRadius = Math.max(newAssignment.getRadius(), 25.0); double apRadius = Math.max(newAssignment.getRadius(), 25.0);
int tries = 0; int tries = 0;
do { do {
destination = new PositionVector(attractionCenter); destination = new PositionVector(attractionCenter);
...@@ -268,7 +278,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -268,7 +278,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
} }
tries++; tries++;
} while (destination == null); } while (destination == null);
currentTarget.put(component, destination); currentTarget.put(component, destination);
} }
...@@ -290,7 +300,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -290,7 +300,7 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
* @param ms * @param ms
* @param destination * @param destination
*/ */
private void doLocalMovement(SimLocationActuator ms, protected void doLocalMovement(SimLocationActuator ms,
PositionVector destination) { PositionVector destination) {
Either<PositionVector, Boolean> either = localMovementStrategy Either<PositionVector, Boolean> either = localMovementStrategy
...@@ -331,6 +341,12 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac ...@@ -331,6 +341,12 @@ public class ModularMovementModel implements MovementModel, EventHandler, Attrac
this.mapVisualization = mapVisualization; this.mapVisualization = mapVisualization;
} }
public void setModelVisualisation(ModularMovementModelViz modelVis)
{
modelVisualisation = modelVis;
modelVisualisation.setMovementModel(this);
}
@Override @Override
public void eventOccurred(Object content, int type) { public void eventOccurred(Object content, int type) {
if (type == EVENT_INIT) { if (type == EVENT_INIT) {
......
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