Commit eff5d429 authored by Julian Zobel's avatar Julian Zobel 🦄
Browse files

Merge branch 'master' into 'cherry-pick-7698d9d7'

# Conflicts:
#   src/de/tud/kom/p2psim/impl/analyzer/metric/output/MetricOutputDAO.java
#   src/de/tud/kom/p2psim/impl/util/db/dao/DAO.java
parents 1c7f20ec 37020b44
......@@ -20,21 +20,12 @@
package de.tud.kom.p2psim.impl.topology.movement;
import java.util.List;
import java.util.Random;
import com.google.common.collect.Lists;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.waypoints.graph.Waypoint;
import de.tud.kom.p2psim.impl.topology.waypoints.graph.WeakWaypoint;
import de.tudarmstadt.maki.simonstrator.api.Randoms;
import de.tud.kom.p2psim.impl.topology.waypoints.RandomWaypointGenerator;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
public class RandomWaypointMovement extends AbstractWaypointMovementModel {
private Random random = Randoms
.getRandom(RandomWaypointMovement.class);
@XMLConfigurableConstructor({"worldX", "worldY"})
public RandomWaypointMovement(double worldX, double worldY) {
......@@ -47,9 +38,11 @@ public class RandomWaypointMovement extends AbstractWaypointMovementModel {
throw new ConfigurationException("SLAWMovementModel requires a valid waypoint model which hasn't been provided, cannot execute");
}
List<Waypoint> waypoints = Lists.newArrayList(waypointModel.getWaypoints(WeakWaypoint.class));
if(!(waypointModel instanceof RandomWaypointGenerator)) {
throw new ConfigurationException("RandomWaypointMovement requires RandomWaypointModel generator which hasn't been provided, cannot execute");
}
nextDestination(component, waypoints.get(random.nextInt(waypoints.size())).getPosition(), 0);
nextDestination(component, ((RandomWaypointGenerator)waypointModel).getRandomPoint(), 0);
}
......
......@@ -40,7 +40,7 @@ import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.api.topology.waypoints.WaypointModel;
import de.tud.kom.p2psim.impl.scenario.simcfg2.annotations.After;
import de.tud.kom.p2psim.impl.scenario.simcfg2.annotations.Configure;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector.DisplayString;
import de.tud.kom.p2psim.impl.topology.waypoints.graph.StrongWaypoint;
......
......@@ -35,14 +35,14 @@ import de.tud.kom.p2psim.api.topology.movement.MovementModel;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.api.topology.placement.PlacementModel;
import de.tud.kom.p2psim.impl.simengine.Simulator;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.topology.TopologyFactory;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.GPSCalculation;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.Binder;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Randoms;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.AttractionPoint;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
/**
*
......@@ -64,34 +64,21 @@ public class StreetMovement implements MovementModel, EventHandler {
private Map<SimLocationActuator, PositionVector> positions = new LinkedHashMap<SimLocationActuator, PositionVector>();
private Map<SimLocationActuator, AttractionPoint> attractionOfClients = new LinkedHashMap<SimLocationActuator, AttractionPoint>();
private Map<SimLocationActuator, IAttractionPoint> attractionOfClients = new LinkedHashMap<SimLocationActuator, IAttractionPoint>();
List<AttractionPoint> attractionPoints = new LinkedList<AttractionPoint>();
List<IAttractionPoint> attractionPoints = new LinkedList<IAttractionPoint>();
private boolean initialized = false;
private long timeBetweenMoveOperation = Simulator.SECOND_UNIT;
private Random rand;
private Random rand = Randoms.getRandom(StreetMovement.class);
private double latLeft; //Values from -90 to 90; always smaller than latRight
private double latRight; //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
public StreetMovement() {
this.worldDimensions = Binder.getComponentOrNull(Topology.class)
.getWorldDimensions();
this.rand = Randoms.getRandom(StreetMovement.class);
latLeft = GPSCalculation.getLatLower();
latRight = GPSCalculation.getLatUpper();
lonLeft = GPSCalculation.getLonLeft();
lonRight = GPSCalculation.getLonRight();
System.out.println("Scheduling first event for Init");
// scheduling initalization!
Event.scheduleImmediately(this, null, EVENT_INIT);
}
......@@ -126,10 +113,10 @@ public class StreetMovement implements MovementModel, EventHandler {
private void initializeStartingPosition(SimLocationActuator ms) {
// set an initial attraction point
int index = rand.nextInt(attractionPoints.size());
AttractionPoint a = attractionPoints.get(index);
IAttractionPoint a = attractionPoints.get(index);
attractionOfClients.put(ms, a);
AttractionPoint b = this.returnNextOrLastAttractionPoint(index);
IAttractionPoint b = this.returnNextOrLastAttractionPoint(index);
PositionVector startingPosition = this.returnRandomPositionBetweenPoints(a,b);
......@@ -146,18 +133,18 @@ public class StreetMovement implements MovementModel, EventHandler {
* @param b Attraction Point B
* @return PositionVector of a randomly selected point on the connecting line of the attraction points
*/
private PositionVector returnRandomPositionBetweenPoints(AttractionPoint a,
AttractionPoint b) {
private PositionVector returnRandomPositionBetweenPoints(IAttractionPoint a,
IAttractionPoint b) {
double longMin, longMax, latMin, latMax, longNew, latNew;
// Points have different longitude, so only search for random value for longitude
if(a.getLongitude() != b.getLongitude()) {
if(a.getLongitude() < b.getLongitude()) {
longMin = a.getLongitude();
longMax = b.getLongitude();
if(a.getLongitudeOrX() != b.getLongitudeOrX()) {
if(a.getLongitudeOrX() < b.getLongitudeOrX()) {
longMin = a.getLongitudeOrX();
longMax = b.getLongitudeOrX();
} else {
longMin = b.getLongitude();
longMax = a.getLongitude();
longMin = b.getLongitudeOrX();
longMax = a.getLongitudeOrX();
}
do {
......@@ -165,17 +152,17 @@ public class StreetMovement implements MovementModel, EventHandler {
} while(longNew < longMin);
assert longNew > longMin && longNew <= longMax;
return new PositionVector(longNew, a.getLatitude());
return new PositionVector(longNew, a.getLatitudeOrY());
}
// Points have different latitude, so only search for random value for latitude
if(a.getLatitude() != b.getLatitude()) {
if(a.getLatitude() < b.getLatitude()) {
latMin = a.getLatitude();
latMax = b.getLatitude();
if(a.getLatitudeOrY() != b.getLatitudeOrY()) {
if(a.getLatitudeOrY() < b.getLatitudeOrY()) {
latMin = a.getLatitudeOrY();
latMax = b.getLatitudeOrY();
} else {
latMin = b.getLatitude();
latMax = a.getLatitude();
latMin = b.getLatitudeOrY();
latMax = a.getLatitudeOrY();
}
do{
......@@ -183,7 +170,7 @@ public class StreetMovement implements MovementModel, EventHandler {
} while(latNew < latMin);
assert latNew > latMin && latNew <= latMax;
return new PositionVector(a.getLongitude(), latNew);
return new PositionVector(a.getLongitudeOrX(), latNew);
}
return null;
......@@ -199,7 +186,7 @@ public class StreetMovement implements MovementModel, EventHandler {
}
@Override
public void changeTargetLocation(SimLocationActuator actuator, AttractionPoint ap) {
public void changeTargetLocation(SimLocationActuator actuator, IAttractionPoint ap) {
/*
* Set a new target AP for the current actuator
*/
......@@ -227,7 +214,7 @@ public class StreetMovement implements MovementModel, EventHandler {
* Move all nodes towards their assigned attraction point
*/
protected void move() {
for (Entry<SimLocationActuator, AttractionPoint> entry : attractionOfClients.entrySet()) {
for (Entry<SimLocationActuator, IAttractionPoint> entry : attractionOfClients.entrySet()) {
SimLocationActuator ms = entry.getKey();
PositionVector attractionCenter = (PositionVector) entry.getValue();
......@@ -288,7 +275,7 @@ public class StreetMovement implements MovementModel, EventHandler {
* @param index Index of the attraction point, for which another attraction point should be returned
* @return Attraction Point
*/
private AttractionPoint returnNextOrLastAttractionPoint(int index) {
private IAttractionPoint returnNextOrLastAttractionPoint(int index) {
boolean updownrand = rand.nextBoolean();
if(updownrand) {
......@@ -309,11 +296,11 @@ public class StreetMovement implements MovementModel, EventHandler {
}
}
public List<AttractionPoint> getAttractionPoints() {
public List<IAttractionPoint> getAttractionPoints() {
return attractionPoints;
}
public void setAttractionPoint(AttractionPoint point) {
public void setAttractionPoint(IAttractionPoint point) {
this.attractionPoints.add(point);
}
}
......@@ -33,7 +33,7 @@ import java.util.stream.Stream;
import de.tud.kom.p2psim.api.topology.movement.MovementModel;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Time;
......
......@@ -24,7 +24,7 @@ import java.util.Set;
import de.tud.kom.p2psim.api.topology.movement.MovementSupported;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
/**
* The Movement Supported Components move to one Position. It is possible to set
......
/*
* 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;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import de.tud.kom.p2psim.api.network.SimNetInterface;
import de.tud.kom.p2psim.api.network.SimNetworkComponent;
import de.tud.kom.p2psim.api.scenario.ConfigurationException;
import de.tud.kom.p2psim.api.topology.movement.MovementModel;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.DefaultTopology;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.IAttractionBasedMovementAnalyzer;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.AttractionPointViz;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.IAttractionProvider;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.mapvisualization.IMapVisualization;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Monitor;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.component.LifecycleComponent;
public class TracefileMovementModel implements MovementModel, EventHandler {
public static String tracefileFolder ="smarter/tracefiles/";
public static String tracefilePrefix = "smarterTraceFile-";
public static long GPS_MISSING_TRACE_THRESHOLD = 60;
public static int ZERO_TO_2 = 0;
public static int _2_to_5 = 0;
public static int _5_to_10 = 0;
public static int TEN_TO_THIRTY = 0;
public static int THIRTY_TO_SIXTY = 0;
public static int SIXTY_TO_120 = 0;
public static int _120_TO_300 = 0;
public static int _300_TO_600 = 0;
public static int ABOVE_600 = 0;
protected final int EVENT_MOVE = 1;
protected final PositionVector DEFAULT_POSITION = new PositionVector(-1000, -1000);
protected long timeBetweenMoveOperations;
protected IMapVisualization mapVisualization;
protected IAttractionProvider attractionGenerator;
protected AttractionPointViz attractionpointVisualization;
protected boolean initialized = false;
private final LinkedHashMap<SimLocationActuator, LinkedList<Step>> components;
private final static LinkedHashMap<SimLocationActuator, Integer> componentToStepSize = new LinkedHashMap<SimLocationActuator, Integer>();
private LinkedList<File> tracefiles;
private boolean first = true;
public TracefileMovementModel() {
components = new LinkedHashMap<SimLocationActuator, LinkedList<Step>>();
tracefiles = new LinkedList<File>();
}
public void initialize() {
if(initialized) {
return;
}
File folder = new File(tracefileFolder);
if (!folder.exists()) {
throw new UnsupportedOperationException("Tracefile folder not found");
}
for(File tracefile : folder.listFiles()) {
if(tracefile.getName().contains(tracefilePrefix)) {
tracefiles.add(tracefile);
}
}
if (mapVisualization != null) {
VisualizationInjector.injectComponent(mapVisualization);
}
if (attractionpointVisualization != null) {
attractionpointVisualization.setAttractionPoints(new LinkedList<>(attractionGenerator.getAttractionPoints()));
VisualizationInjector.injectComponent(attractionpointVisualization);
}
initialized = true;
Event.scheduleWithDelay(timeBetweenMoveOperations, this, null, EVENT_MOVE);
}
@Override
public void eventOccurred(Object content, int type) {
if (type == EVENT_MOVE) {
move();
}
}
private void move() {
if(first) {
components.forEach((component, steps) -> {
shutdownComponent(component);
});
}
// as the files contain the timestamp in seconds, the current time needs to be converted in seconds
long currentTime = Time.getCurrentTime() / Time.SECOND;
LinkedList<SimLocationActuator> toRemove = new LinkedList<>();
components.forEach((component, steps) -> {
Step step = steps.peek();
if (step != null) {
if(currentTime < step.timestamp) {
if(DefaultTopology.isWithinWorldBoundaries(component.getRealPosition())) {
if(currentTime + GPS_MISSING_TRACE_THRESHOLD >= step.timestamp) {
if(component.getRealPosition().distanceTo(new PositionVector(step.x, step.y)) > GPS_MISSING_TRACE_THRESHOLD * 2) {
shutdownComponent(component);
}
// do nothing for smoothing?
// TODO
else {
int stepcount = componentToStepSize.get(component);
stepcount++;
componentToStepSize.put(component, stepcount);
}
}
else {
if(DefaultTopology.isWithinWorldBoundaries(component.getRealPosition())) {
// TODO
long delta = step.timestamp - currentTime + GPS_MISSING_TRACE_THRESHOLD;
if(delta > 0 && delta <= 2) {
ZERO_TO_2++;
}
else if(delta > 2 && delta <= 5) {
_2_to_5++;
}
else if(delta > 5 && delta <= 10) {
_5_to_10++;
}
else if(delta > 10 && delta <= 30) {
TEN_TO_THIRTY++;
}
else if(delta > 30 && delta <= 60) {
THIRTY_TO_SIXTY++;
}
else if(delta > 60 && delta <= 120) {
SIXTY_TO_120++;
}
else if(delta > 120 && delta <= 300) {
_120_TO_300++;
}
else if(delta > 300 && delta <= 600) {
_300_TO_600++;
}
else if(delta > 600) {
ABOVE_600++;
}
shutdownComponent(component);
}
}
}
}
else {
step = steps.pop();
// TODO
int stepcount = componentToStepSize.get(component);
stepcount++;
componentToStepSize.put(component, stepcount);
if(!DefaultTopology.isWithinWorldBoundaries(component.getRealPosition())) {
component.updateCurrentLocation(new PositionVector(step.x, step.y));
startupComponent(component);
}
else {
component.updateCurrentLocation(new PositionVector(step.x, step.y));
}
}
}
else {
toRemove.add(component);
}
});
for (SimLocationActuator simLocationActuator : toRemove) {
components.remove(simLocationActuator);
shutdownComponent(simLocationActuator);
}
Event.scheduleWithDelay(timeBetweenMoveOperations, this, null, EVENT_MOVE);
if(first) {
// Inform analyzer of resolved movement
if(Monitor.hasAnalyzer(IAttractionBasedMovementAnalyzer.class)) {
Monitor.getOrNull(IAttractionBasedMovementAnalyzer.class).onAllNodeInitializationCompleted(components.keySet());
}
first =false;
}
}
private void shutdownComponent(SimLocationActuator node) {
SimNetworkComponent net = (SimNetworkComponent) node.getHost().getNetworkComponent();
for (SimNetInterface netI : net.getSimNetworkInterfaces()) {
if (netI.isOnline()) {
netI.goOffline();
}
}
try {
List<LifecycleComponent> comps = node.getHost().getComponents(LifecycleComponent.class);
for (LifecycleComponent lifecycleComponent : comps) {
lifecycleComponent.stopComponent();
}
} catch (Exception e) {
// TODO: handle exception
}
node.updateCurrentLocation(new PositionVector(DEFAULT_POSITION));
}
private void startupComponent(SimLocationActuator node) {
SimNetworkComponent net = (SimNetworkComponent) node.getHost().getNetworkComponent();
for (SimNetInterface netI : net.getSimNetworkInterfaces()) {
if (netI.isOffline()) {
netI.goOnline();
}
}
try {
List<LifecycleComponent> comps = node.getHost().getComponents(LifecycleComponent.class);
for (LifecycleComponent lifecycleComponent : comps) {
lifecycleComponent.startComponent();
}
} catch (Exception e) {
// TODO: handle exception
}
}
@Override
public void addComponent(SimLocationActuator actuator) {
if (!initialized) {
initialize();
}
associateTracefile(actuator);
}
private void associateTracefile(SimLocationActuator actuator) {
if(tracefiles.isEmpty()) {
throw new UnsupportedOperationException("List of tracefiles is empty, thus cannot initiate the component!");
}
if(components.containsKey(actuator)) {
throw new UnsupportedOperationException("Component was already assigned!");
}
LinkedList<Step> stepQueue = new LinkedList<Step>();
File tracefile = tracefiles.pop();
Scanner filescanner;
try {
filescanner = new Scanner(tracefile);
while(filescanner.hasNextLine()) {
String line = filescanner.nextLine();
String[] split = line.split(" ");
long timestamp = Long.valueOf(split[0]);
//double lat = Double.valueOf(split[1]);
//double lon = Double.valueOf(split[2]);
double x = Double.valueOf(split[3]);
double y = Double.valueOf(split[4]);
Step s = new Step(timestamp, x, y);
stepQueue.add(s);
}
filescanner.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
catch (Exception e) {
System.err.println(tracefile.getName());
e.printStackTrace();
}
// System.out.println(actuator.getHost().getId() + " <> " + tracefile.getName());
components.put(actuator, stepQueue);
componentToStepSize.put(actuator, 0);
}
public static int getComponentStepSize(SimLocationActuator component) {
if(!componentToStepSize.containsKey(component)) {
return -1;
}
return componentToStepSize.get(component);
}
@Override
public void placeComponent(SimLocationActuator actuator) {
// Initial placement
actuator.updateCurrentLocation(new PositionVector(DEFAULT_POSITION));
}
@Override
public void setTimeBetweenMoveOperations(long time) {
this.timeBetweenMoveOperations = time;
}
public void setIMapVisualization(IMapVisualization mapVisualization) {
this.mapVisualization = mapVisualization;
}
public void setAttractionPointViz(AttractionPointViz viz) {
this.attractionpointVisualization = viz;
}
public void setIAttractionGenerator(IAttractionProvider attractionGenerator) {
if (attractionGenerator == null) {
throw new ConfigurationException(
"AttractionGenerator is missing in ModularMovementModel!");
}
this.attractionGenerator = attractionGenerator;
}
private class Step {
public final double x;
public final double y;
public final long timestamp;
public Step(long timestamp, double x, double y) {
this.x = x;
this.y = y;
this.timestamp = timestamp;
}
@Override
public String toString() {
return "Step @ " + timestamp + "s -> ("+x+", "+y+")";
}
}
}
......@@ -32,9 +32,9 @@ import de.tud.kom.p2psim.api.network.SimNetInterface;
import de.tud.kom.p2psim.api.topology.movement.MovementModel;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.network.routed.RoutedNetLayer;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.topology.movement.vehicular.sumo.simulation.controller.traci.TraciSimulationController;
import de.tud.kom.p2psim.impl.topology.movement.vehicular.sumo.simulation.controller.xml.XMLSimulationController;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.vehicular.DefaultVehicleInformationComponent;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
......@@ -60,6 +60,8 @@ public class VehicleMovementModel implements MovementModel, EventHandler {
private static VehicleMovementModel MOVEMENT;
public static final int TIMESTEP_RATIO = 1;
private long timeBetweenMoveOperations;
private final List<SimLocationActuator> components;
......@@ -75,16 +77,11 @@ public class VehicleMovementModel implements MovementModel, EventHandler {
private final int width;
private final int height;
private double scenarioWidth = 0;
private double scenarioHeight = 0;
private final String sumoExe;
private final String sumoConfigFile;
private final String sumoTrace;
private String sumoIntersections;
private final long timestepConversion = Time.SECOND;
private boolean initialized = false;
private VehicleController _controller;
......@@ -92,6 +89,8 @@ public class VehicleMovementModel implements MovementModel, EventHandler {
private double _percentageOfKnownRoutes = 1;
private int _startTime;
/**
* Constructor for the movement model using the sumo TraCI API
* @param timeBetweenMoveOperations The time between two movement operations.
......@@ -117,6 +116,27 @@ public class VehicleMovementModel implements MovementModel, EventHandler {
this.offsetY = Integer.parseInt(offsetY);
this.width = Integer.parseInt(width);
this.height = Integer.parseInt(height);
Thread current = Thread.currentThread();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
StackTraceElement[] stackTrace = current.getStackTrace();
System.out.println();
System.out.println();
for (int i = 0; i < stackTrace.length; i++) {
System.out.println(stackTrace[i]);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
}
});
// thread.start();
}
/**
......@@ -154,6 +174,10 @@ public class VehicleMovementModel implements MovementModel, EventHandler {
_percentageOfKnownRoutes = pPercentageOfKnownRoutes;
}
public void setStartTime(long pStartTime) {
_startTime = (int) (pStartTime / timeBetweenMoveOperations);
}
public void setReuseComponents(boolean pReuseComponents) {
_reuseComponents = pReuseComponents;
......@@ -215,14 +239,15 @@ public class VehicleMovementModel implements MovementModel, EventHandler {
protected void initializeModel() {
// Schedule first step
if (!initialized) {
Event.scheduleWithDelay(timeBetweenMoveOperations, this, null, 0);
Event.scheduleWithDelay(timeBetweenMoveOperations * TIMESTEP_RATIO, this, null, 0);
if (sumoExe != null) {
TraciSimulationController simulationController = TraciSimulationController.createSimulationController(sumoExe, sumoConfigFile);
TraciSimulationController simulationController = TraciSimulationController.createSimulationController(sumoExe, sumoConfigFile, TIMESTEP_RATIO);
_controller = simulationController;
_controller.setObservedArea(offsetX, offsetY, offsetX + width, offsetY + height);
_controller.init();
_controller.nextStep();
simulationController.setStartTime(_startTime);
_controller.init(timeBetweenMoveOperations);
_controller.nextStep(timeBetweenMoveOperations);
_extractor = simulationController;
} else {
......@@ -235,15 +260,12 @@ public class VehicleMovementModel implements MovementModel, EventHandler {
_controller = simulationController;
_controller.setObservedArea(offsetX, offsetY, offsetX + width, offsetY + height);
_controller.init();
_controller.nextStep();
_controller.init(timeBetweenMoveOperations);
_controller.nextStep(timeBetweenMoveOperations);
_extractor = simulationController;
}
scenarioWidth = _extractor.getScenarioWidth();
scenarioHeight = _extractor.getScenarioHeight();
System.out.println("Initialization: done.");
}
}
......@@ -260,10 +282,12 @@ public class VehicleMovementModel implements MovementModel, EventHandler {
* simulation performance due to less recalculations in the network
* models.
*/
long currentTime = Time.getCurrentTime() / timestepConversion;
long currentTime = Time.getCurrentTime() / timeBetweenMoveOperations;
System.out.println("Performing movement for step " + currentTime);
while (_controller.getStep() - _controller.getStart() < currentTime) {
if (!_controller.nextStep()) {
if (!_controller.nextStep(timeBetweenMoveOperations)) {
return;
}
}
......@@ -293,7 +317,7 @@ public class VehicleMovementModel implements MovementModel, EventHandler {
e.printStackTrace();
}
component.updateCurrentLocation(new PositionVector(position.getLongitude(), position.getLatitude()));
component.updateCurrentLocation(new PositionVector(position.getLongitudeOrX(), position.getLatitudeOrY()));
component.setMovementSpeed(_controller.getVehicleSpeed(vehicle));
......@@ -316,7 +340,7 @@ public class VehicleMovementModel implements MovementModel, EventHandler {
}
// Reschedule next step
Event.scheduleWithDelay(timeBetweenMoveOperations, this, null, 0);
Event.scheduleWithDelay(timeBetweenMoveOperations * TIMESTEP_RATIO, this, null, 0);
}
/**
......@@ -385,9 +409,11 @@ public class VehicleMovementModel implements MovementModel, EventHandler {
idComponentMatcher.put(vehicle, simLocationActuator);
hostVehicleIDMatching.put(simLocationActuator.getHost().getId(), vehicle);
} else {
if (idComponentMatcher.size() != 0) {
throw new RuntimeException("Unable to assign new components. Please increase node amount" + (_reuseComponents?".":" or enable the reuse of components."));
}
}
}
return idComponentMatcher.get(vehicle);
}
......
......@@ -18,24 +18,14 @@
*
*/
package de.tud.kom.p2psim.impl.topology.waypoints.graph;
package de.tud.kom.p2psim.impl.topology.movement.aerial;
import org.jgrapht.EdgeFactory;
import de.tud.kom.p2psim.api.topology.movement.UAVMovementModel;
import de.tud.kom.p2psim.impl.topology.component.UAVTopologyComponent;
/**
* Creates a simple Path from two given way points.
*
* @author Fabio Zöllner
* @version 1.0, 09.04.2012
*/
public class PathEdgeFactory implements EdgeFactory<Waypoint, Path> {
@Override
public Path createEdge(Waypoint sourceVertex, Waypoint targetVertex) {
// Path uses the distance as weight by default
Path path = new Path(sourceVertex, targetVertex);
return path;
}
public interface AerialMovementModelFactory {
public UAVMovementModel createComponent(UAVTopologyComponent topologyComponent);
}
/*
* 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.aerial;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import org.apache.commons.math3.geometry.euclidean.twod.Vector2D;
import de.tud.kom.p2psim.api.topology.movement.UAVMovementModel;
import de.tud.kom.p2psim.impl.energy.components.ActuatorComponent;
import de.tud.kom.p2psim.impl.energy.components.StatelessActuatorComponent;
import de.tud.kom.p2psim.impl.topology.component.UAVTopologyComponent;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.util.db.metric.CustomMeasurement;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.uavsupport.callbacks.ReachedLocationCallback;
/**
* Simplified thrust-based local movement model based on the Intel Aero UAV.
*
* The movement logic uses only straight forward movement with the maximum speed available.
*
* TODO Acceleration
* TODO Movement model for plane-like UAVs
*
* @author Julian Zobel
* @version 1.0, 11.09.2018
*/
public class MulticopterMovement implements UAVMovementModel {
private UAVTopologyComponent topologyComponent;
private double currentAngleOfAttack;
private double velocity;
private double targetVelocity;
private LinkedList<PositionVector> route = new LinkedList<>();
private Map<PositionVector, ReachedLocationCallback> locationCallbacks = new LinkedHashMap<>(); // TODO callback interface
private StatelessActuatorComponent motor;
private double mass; // kg
private double areaTop; // m^2
private double areaFront; // m^2
private double dragCoefficient;
private double maximumPitchAngleAllowed; // ° max angle
private double maximumDecentVelocityAllowed; // m/s
// TODO currently not used
private double maximumTurnAngle; // 90° per second turn angle
public MulticopterMovement(UAVTopologyComponent topologyComponent, double massTotal,
double areaTop, double areaFront, double UAVDragCoefficient, double maximumPitchAngleAllowed,
double maximumDecentVelocityAllowed, double maximumTurnAngle) {
this.topologyComponent = topologyComponent;
this.mass = massTotal;
this.areaTop = areaTop;
this.areaFront = areaFront;
this.dragCoefficient = UAVDragCoefficient;
this.maximumPitchAngleAllowed = maximumPitchAngleAllowed;
this.maximumDecentVelocityAllowed = maximumDecentVelocityAllowed;
this.maximumTurnAngle = maximumTurnAngle;
}
boolean first = true;
@Override
public void move(long timeBetweenMovementOperations) {
if(motor.isOn() && !route.isEmpty()) {
PositionVector position = new PositionVector(topologyComponent.getRealPosition());
PositionVector target = route.getFirst();
double distanceToTargetPosition = position.distanceTo(target);
// If target point is reached within a 1 meter margin, we remove that point from the list
if(distanceToTargetPosition < 0.1 || distanceToTargetPosition < velocity)
{
target = route.removeFirst();
if(route.isEmpty()) {
// go to hover mode
topologyComponent.updateCurrentLocation(target.clone());
velocity = 0;
motor.requestThrust(hoverThrustRequired());
PositionVector direction = topologyComponent.getCurrentDirection().clone();
direction.setEntry(2, 0);
topologyComponent.updateCurrentDirection(direction);
locationReached(topologyComponent.getCurrentLocation());
return;
}
else {
// get to speed
if(targetVelocity > 0 && targetVelocity < getHorizontalMaxVelocity()) {
motor.requestThrust(estimateRequiredThrust(targetVelocity));
velocity = targetVelocity;
}
else {
motor.requestThrust(getHorizontalMaxVelocityRequiredTotalThrust());
velocity = getHorizontalMaxVelocity();
}
long timeUntilReachedLocation = (long) (distanceToTargetPosition / velocity) * Time.SECOND;
target = route.getFirst();
PositionVector directionToTarget = new PositionVector(target);
directionToTarget.subtract(position);
double timefactor = timeUntilReachedLocation / Time.SECOND;
directionToTarget.normalize();
topologyComponent.updateCurrentDirection(directionToTarget.clone());
directionToTarget.multiplyScalar(velocity * timefactor);
PositionVector newPosition = new PositionVector(position);
newPosition.add(directionToTarget);
topologyComponent.updateCurrentLocation(newPosition);
if(timeUntilReachedLocation < timeBetweenMovementOperations) {
this.move(timeBetweenMovementOperations - timeUntilReachedLocation);
}
}
}
else {
double timefactor = timeBetweenMovementOperations / Time.SECOND;
// get to speed
if(targetVelocity > 0 && targetVelocity < getHorizontalMaxVelocity()) {
motor.requestThrust(estimateRequiredThrust(targetVelocity));
velocity = targetVelocity;
}
else {
motor.requestThrust(getHorizontalMaxVelocityRequiredTotalThrust());
velocity = getHorizontalMaxVelocity();
}
PositionVector directionToTarget = new PositionVector(target);
directionToTarget.subtract(position);
directionToTarget.normalize();
if(directionToTarget.getX() != 0 || directionToTarget.getY() != 0) {
topologyComponent.updateCurrentDirection(directionToTarget.clone());
}
directionToTarget.multiplyScalar(velocity * timefactor);
PositionVector newPosition = new PositionVector(position);
newPosition.add(directionToTarget);
topologyComponent.updateCurrentLocation(newPosition);
}
}
else if(motor.isOn()) {
if(velocity != 0) {
throw new UnsupportedOperationException("no route but speed not 0?");
}
PositionVector position = new PositionVector(topologyComponent.getRealPosition());
if(position.getAltitude() == 0) {
motor.requestThrust(0);
}
else {
motor.requestThrust(hoverThrustRequired());
}
}
}
/*
*
*/
protected double verticalDescentMaxThrust() {
// m * g - 0.5 * p * C * A * v^2
return hoverThrustRequired() - 0.5 * bodyDrag(0, new PositionVector(0,0,1)) * maximumDecentVelocityAllowed * maximumDecentVelocityAllowed;
}
protected double verticalAscentMaxAcceleration() {
return (motor.getMaxThrust() - hoverThrustRequired()) / mass;
}
@Override
public double getVerticalAscentMaxVelocity() {
double maxThrust = motor.getMaxThrust();
return Math.sqrt(2.0 * (maxThrust - hoverThrustRequired()) / bodyDrag(0, new PositionVector(0,0,1)));
}
protected double hoverThrustRequired() {
return mass * GRAVITY;
}
@Override
public double getHorizontalMaxVelocity() {
double horizontalThrust = getHorizontalComponentMaxThrust();
double maxVelocity = Math.sqrt( (2.0 * horizontalThrust) / bodyDrag(maximumPitchAngleAllowed, new PositionVector(1,0,0)));
return maxVelocity;
}
protected double getHorizontalComponentMaxThrust() {
// hoverthrust / cos => amount of thrust in horizonal direction with °angle
double stableAltitudeMaximumTotalThrust = getHorizontalMaxVelocityRequiredTotalThrust();
// fraction of total thrust in horizonal (forward) direction with °angle
double maximumHorizontalThrustStableAltitude = stableAltitudeMaximumTotalThrust * Math.sin(maximumPitchAngleAllowed);
return maximumHorizontalThrustStableAltitude;
}
protected double getHorizontalMaxVelocityRequiredTotalThrust() {
return hoverThrustRequired() / Math.cos(maximumPitchAngleAllowed);
}
protected double bodyDrag(double angleRadians, PositionVector direction) {
return AIRDENSITY * dragCoefficient * areaExposedToDrag(angleRadians, direction);
}
protected double areaExposedToDrag(double angleRadians, PositionVector direction) {
Vector2D v = new Vector2D(Math.abs(direction.getX()) + Math.abs(direction.getY()), Math.abs(direction.getZ()));
v = v.normalize();
double areaExposedFront = v.getX() * (Math.sin(angleRadians) * areaTop + Math.cos(angleRadians) * areaFront );
double areaExposedTop = v.getY() * (Math.cos(angleRadians) * areaTop + Math.sin(angleRadians) * areaFront);
return areaExposedFront + areaExposedTop;
}
/*
* F_drag [N] = 0.5 * p * C_drag * A * v^2
*/
protected double currentDrag() {
return 0.5 * bodyDrag(currentAngleOfAttack, topologyComponent.getCurrentDirection()) * velocity * velocity;
}
/**
* Calculate the drag induced on the UAV with a given velocity and an angle of attack (in radians) moving forward horizontally.
*
* @param velocity
* @param angleInRadians
* @return
*/
protected double forwardDrag(double velocity, double angleInRadians) {
return 0.5 * bodyDrag(angleInRadians, new PositionVector(1,0,0)) * velocity * velocity;
}
/*
*
*/
@Override
public void setMotorControl(ActuatorComponent motor) {
this.motor = (StatelessActuatorComponent) motor;
}
@Override
public void setTargetVelocity(double v_pref) {
this.targetVelocity = v_pref;
}
@Override
public double getCurrentVelocity() {
return velocity;
}
/**
* Trigger the callback function, if there is a valid callback
*
* @param position
*/
private void locationReached(PositionVector position) {
if(locationCallbacks.containsKey(position)) {
locationCallbacks.get(position).reachedLocation();
}
}
@Override
public void setTargetLocation(PositionVector target,
ReachedLocationCallback reachedLocationCallback) {
route.clear();
route.add(target);
if(reachedLocationCallback != null)
locationCallbacks.put(target, reachedLocationCallback);
}
@Override
public void setTargetLocationRoute(LinkedList<PositionVector> route,
ReachedLocationCallback reachedLocationCallback) {
this.route.clear();
this.route.addAll(route);
if(reachedLocationCallback != null)
locationCallbacks.put(route.getLast(), reachedLocationCallback);
}
@Override
public void addTargetLocation(PositionVector target,
ReachedLocationCallback reachedLocationCallback) {
route.add(target);
if(reachedLocationCallback != null)
locationCallbacks.put(target, reachedLocationCallback);
}
@Override
public LinkedList<PositionVector> getTargetLocations() {
LinkedList<PositionVector> copy = new LinkedList<>();
for (PositionVector pv : route) {
copy.add(pv.clone());
}
return copy;
}
@Override
public void removeTargetLocations() {
route.clear();
locationCallbacks.clear();
}
@Override
public double getHorizontalMinVelocity() {
return Math.sqrt(2 * hoverThrustRequired() * Math.tan(Math.toRadians(0.25)) / bodyDrag(Math.toRadians(0.25), new PositionVector(1,0,0)));
}
@Override
public double estimatePowerConsumptionWatt(double velocity) {
if(velocity == 0) {
return motor.estimatePowerConsumptionWatt(hoverThrustRequired());
}
else if(velocity > getHorizontalMaxVelocity()) {
return -1;
}
else if(velocity < getHorizontalMinVelocity()) {
return -1;
}
else {
double requiredThrust = estimateRequiredThrust(velocity);
double wattage = motor.estimatePowerConsumptionWatt(requiredThrust);
return wattage;
}
}
protected double estimateRequiredThrust(double velocity) {
if(velocity == 0) {
return motor.estimatePowerConsumptionWatt(hoverThrustRequired());
}
else if(velocity > getHorizontalMaxVelocity()) {
return -1;
}
else if(velocity < getHorizontalMinVelocity()) {
return -1;
}
else {
double estimateAngle = estimatePitchAngleForVelocity(velocity);
double estimatedDrag = forwardDrag(velocity, estimateAngle);
double requiredThrust = Math.sqrt(hoverThrustRequired() * hoverThrustRequired() + estimatedDrag * estimatedDrag);
return requiredThrust;
}
}
/**
* Estimate the pitch angle (angle of attack) required to get the target velocity.
* Angle precision is 1/4 degree.
*
* @param velocity
* @return
*/
protected double estimatePitchAngleForVelocity(double velocity) {
int low = 0;
int high = Integer.MAX_VALUE;
double vsquared = (velocity * velocity);
for(int i = 0; i <= ((int) Math.toDegrees(maximumPitchAngleAllowed)); i++) {
double v2 = 2 * hoverThrustRequired() * Math.tan(Math.toRadians(i)) / bodyDrag(Math.toRadians(i), new PositionVector(1,0,0));
if(v2 > vsquared && i < high) {
high = i;
}
else if(v2 < vsquared && i >= low) {
low = i;
}
else if(v2 == vsquared ) {
return Math.toRadians(i);
}
}
if(high < Integer.MAX_VALUE) {
double lo = low;
double hi = high;
double nearest = -1;
double nearestDiff = Double.MAX_VALUE;
double step = (hi - lo) / 4;
for(int i = 0; i < 4; i++) {
double d = lo + i * step;
double v2 = 2 * hoverThrustRequired() * Math.tan(Math.toRadians(d)) / bodyDrag(Math.toRadians(d), new PositionVector(1,0,0));
double diff = Math.abs(((velocity * velocity) - v2));
if(diff < nearestDiff || (lo == 0 && i == 1)) {
nearestDiff = diff;
nearest = d;
}
}
return Math.toRadians(nearest);
}
return maximumPitchAngleAllowed;
}
/**
* Factory for this movement model
*
* @author Julian Zobel
* @version 1.0, 14.01.2020
*/
public static class Factory implements AerialMovementModelFactory {
private double massTotal = 1.465; // kg
private double areaTop = 0.245; // m^2
private double areaFront = 0.1; // m^2
private double UAVDragCoefficient = 0.7;
private double maximumPitchAngleAllowed = Math.toRadians(60); // ° max angle
private double maximumDecentVelocityAllowed = 5; // m/s
private double maximumTurnAngle = Math.toRadians(90); // 90° per second turn angle
public void setMassTotal(double massTotal) {
this.massTotal = massTotal;
}
public void setAreaTop(double areaTop) {
this.areaTop = areaTop;
}
public void setAreaFront(double areaFront) {
this.areaFront = areaFront;
}
public void setUAVDragCoefficient(double uAVDragCoefficient) {
UAVDragCoefficient = uAVDragCoefficient;
}
public void setMaximumPitchAngleAllowed(double maximumPitchAngleAllowed) {
this.maximumPitchAngleAllowed = Math.toRadians(maximumPitchAngleAllowed);
}
public void setMaximumDecentVelocityAllowed(
double maximumDecentVelocityAllowed) {
this.maximumDecentVelocityAllowed = maximumDecentVelocityAllowed;
}
public void setMaximumTurnAngle(double maximumTurnAngle) {
this.maximumTurnAngle = Math.toRadians(maximumTurnAngle);
}
public UAVMovementModel createComponent(UAVTopologyComponent topologyComponent) {
return new MulticopterMovement(topologyComponent, massTotal, areaTop, areaFront,
UAVDragCoefficient, maximumPitchAngleAllowed, maximumDecentVelocityAllowed, maximumTurnAngle);
}
}
// For evaluation only!
@Entity
@Table(name = "energyModel")
public static class EnergyModelPropertyMeasurement extends CustomMeasurement {
/*
* For DB performance reasons, we only store up to 15 characters of the
* topic (longer topics are truncated)
*/
@Column(nullable = false, name = "[velocity]")
final float velocity;
@Column(nullable = false, name = "[thrust]")
final float thrust;
@Column(nullable = false, name = "[ampere]")
final float ampere;
@Column(nullable = false, name = "[watt]")
final float watt;
@Column(nullable = false, name = "[wattPerMeter]")
final float wattperdistance;
public EnergyModelPropertyMeasurement(double velocity, double thrust, double ampere, double watt) {
/*
* Store all relevant fields
*/
this.velocity = (float) velocity;
this.thrust = (float) thrust;
this.ampere = (float) ampere;
this.watt = (float) watt;
if (velocity == 0) {
this.wattperdistance = -1;
} else
this.wattperdistance = this.watt / this.velocity;
}
public static EnergyModelPropertyMeasurement getPropoertyMeasurement(MulticopterMovement m, double velocity) {
double th = m.estimateRequiredThrust(velocity);
if (th == -1) {
return null;
}
double w = m.estimatePowerConsumptionWatt(velocity);
double amp = w / 14.8;
return new EnergyModelPropertyMeasurement(velocity, th, amp, w);
}
}
}
/*
* 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.aerial;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import de.tud.kom.p2psim.api.topology.movement.UAVMovementModel;
import de.tud.kom.p2psim.impl.energy.components.ActuatorComponent;
import de.tud.kom.p2psim.impl.energy.components.StatefulActuatorComponent;
import de.tud.kom.p2psim.impl.topology.component.UAVTopologyComponent;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tudarmstadt.maki.simonstrator.api.Time;
import de.tudarmstadt.maki.simonstrator.api.uavsupport.callbacks.ReachedLocationCallback;
/**
* Local movement for UAVs.
* This simple movement logic uses straight forward movement with the set speed (up to a max).
* Energy consumption is based on state actuators.
*
* @author Julian Zobel
* @version 1.1, 14.01.2020
*/
public class SimpleMulticopterMovement implements UAVMovementModel {
private UAVTopologyComponent topologyComponent;
private double velocity;
private double targetVelocity;
private double maximumHorizontalVelocity;
private double maximumVerticalVelocity;
// TODO currently not used
private double minimumHorizontalVelocity;
private double minimumVerticalVelocity;
private LinkedList<PositionVector> route = new LinkedList<>();
private Map<PositionVector, ReachedLocationCallback> locationCallbacks = new LinkedHashMap<>();
private StatefulActuatorComponent motor;
public SimpleMulticopterMovement(UAVTopologyComponent topologyComponent, double maximumHorizontalVelocity,
double maximumVerticalVelocity, double minimumHorizontalVelocity, double minimumVerticalVelocity) {
this.topologyComponent = topologyComponent;
this.maximumHorizontalVelocity = maximumHorizontalVelocity;
this.maximumVerticalVelocity = maximumVerticalVelocity;
this.minimumHorizontalVelocity = minimumHorizontalVelocity;
this.minimumVerticalVelocity = minimumVerticalVelocity;
}
@Override
public void move(long timeBetweenMovementOperations) {
if(motor.isOn() && !route.isEmpty()) {
PositionVector currentPosition = topologyComponent.getRealPosition();
PositionVector targetPosition = route.getFirst();
Double distanceToTargetPosition = targetPosition.distanceTo(currentPosition);
// If target point is reached within a 1 meter margin, we remove that point from the list
if(distanceToTargetPosition < 0.1 || distanceToTargetPosition < velocity)
{
route.removeFirst();
topologyComponent.updateCurrentLocation(targetPosition); // triggers energy consumption for last distance
velocity = 0;
motor.useActuator(0);
locationReached(targetPosition);
return;
}
double timefactor = timeBetweenMovementOperations / Time.SECOND;
velocity = Math.min(distanceToTargetPosition, targetVelocity);
PositionVector direction = new PositionVector(targetPosition);
direction.subtract(currentPosition);
direction.normalize();
direction.multiplyScalar(velocity * timefactor);
PositionVector newPosition = new PositionVector(currentPosition);
newPosition.add(direction);
motor.useActuator(1);
topologyComponent.updateCurrentLocation(newPosition);
}
else if(motor.isOn()) {
if(velocity != 0) {
throw new UnsupportedOperationException("no route but speed not 0?");
}
PositionVector position = new PositionVector(topologyComponent.getRealPosition());
if(position.getAltitude() == 0) {
motor.turnOff();
}
else {
motor.useActuator(0);
}
}
}
@Override
public void setTargetVelocity(double v_pref) {
this.targetVelocity = v_pref;
}
@Override
public double getCurrentVelocity() {
return velocity;
}
/**
* Trigger the callback function, if there is a valid callback
*
* @param position
*/
private void locationReached(PositionVector position) {
if(locationCallbacks.containsKey(position)) {
locationCallbacks.get(position).reachedLocation();
}
}
@Override
public void setTargetLocation(PositionVector target,
ReachedLocationCallback reachedLocationCallback) {
route.clear();
route.add(target);
if(reachedLocationCallback != null)
locationCallbacks.put(target, reachedLocationCallback);
}
@Override
public void setTargetLocationRoute(LinkedList<PositionVector> route,
ReachedLocationCallback reachedLocationCallback) {
this.route.clear();
this.route.addAll(route);
if(reachedLocationCallback != null)
locationCallbacks.put(route.getLast(), reachedLocationCallback);
}
@Override
public void addTargetLocation(PositionVector target,
ReachedLocationCallback reachedLocationCallback) {
route.add(target);
if(reachedLocationCallback != null)
locationCallbacks.put(target, reachedLocationCallback);
}
@Override
public LinkedList<PositionVector> getTargetLocations() {
LinkedList<PositionVector> copy = new LinkedList<>();
for (PositionVector pv : route) {
copy.add(pv.clone());
}
return copy;
}
@Override
public void removeTargetLocations() {
route.clear();
locationCallbacks.clear();
}
@Override
public double estimatePowerConsumptionWatt(double velocity) {
double wattage = 0;
if(velocity == 0) {
wattage = motor.estimateEnergyConsumptionWatt(0);
}
else {
wattage = motor.estimateEnergyConsumptionWatt(1.0);
}
return wattage;
}
@Override
public void setMotorControl(ActuatorComponent motor) {
this.motor = (StatefulActuatorComponent) motor;
}
@Override
public double getVerticalAscentMaxVelocity() {
return maximumVerticalVelocity;
}
@Override
public double getHorizontalMaxVelocity() {
return maximumHorizontalVelocity;
}
@Override
public double getHorizontalMinVelocity() {
return 0;
}
/**
* Factory for this movement model
*
* @author Julian Zobel
* @version 1.0, 14.01.2020
*/
public static class Factory implements AerialMovementModelFactory {
private double maximumHorizontalVelocity = 15;
public void setMaximumHorizontalVelocity(double maximumHorizontalVelocity) {
this.maximumHorizontalVelocity = maximumHorizontalVelocity;
}
private double maximumVerticalVelocity = 5;
public void setMaximumVerticalVelocity(double maximumVerticalVelocity) {
this.maximumVerticalVelocity = maximumVerticalVelocity;
}
private double minimumHorizontalVelocity = 15;
public void setMinimumHorizontalVelocity(double minimumHorizontalVelocity) {
this.minimumHorizontalVelocity = minimumHorizontalVelocity;
}
private double minimumVerticalVelocity = 5;
public void setMinimumVerticalVelocity(double minimumVerticalVelocity) {
this.minimumVerticalVelocity = minimumVerticalVelocity;
}
public UAVMovementModel createComponent(UAVTopologyComponent topologyComponent) {
return new SimpleMulticopterMovement(topologyComponent, maximumHorizontalVelocity,
maximumVerticalVelocity, minimumHorizontalVelocity, minimumVerticalVelocity);
}
}
}
......@@ -18,67 +18,61 @@
*
*/
package de.tud.kom.p2psim.impl.topology.waypoints.graph;
package de.tud.kom.p2psim.impl.topology.movement.aerial;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.LinkedList;
import java.util.List;
import org.jgrapht.EdgeFactory;
import org.jgrapht.graph.SimpleWeightedGraph;
import de.tud.kom.p2psim.api.topology.movement.MovementModel;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.api.topology.movement.SimUAVLocationActuator;
import de.tudarmstadt.maki.simonstrator.api.Event;
import de.tudarmstadt.maki.simonstrator.api.EventHandler;
import de.tudarmstadt.maki.simonstrator.api.Time;
/**
* The simple weighted edge retrievable graph caches the edges
* for later batch retrieval.
* Movement model container for all UAVs. This primarily triggers the local movement models of the respective UAVs.
*
* It uses the WeakHashMap to avoid unintentional references to
* dead objects.
*
* @author Fabio Zöllner
* @version 1.0, 27.03.2012
* @param <V> Vertices
* @param <E> Edges
* @author Julian Zobel
* @version 1.0, 6 Sep 2018
*/
public class DefaultWeightedEdgeRetrievableGraph<V, E> extends SimpleWeightedGraph<V, E> {
private WeakHashMap<E, Object> edgeMap = new WeakHashMap<E, Object>();
public class UAVMovement implements MovementModel, EventHandler {
private List<SimUAVLocationActuator> actuators = new LinkedList<>();
protected long timeBetweenMoveOperation = Time.SECOND;
@SuppressWarnings("unchecked")
public DefaultWeightedEdgeRetrievableGraph(EdgeFactory edgefactory) {
super(edgefactory);
public UAVMovement() {
Event.scheduleImmediately(this, null, 0);
}
@SuppressWarnings("unchecked")
public DefaultWeightedEdgeRetrievableGraph(Class class1) {
super(class1);
@Override
public void addComponent(SimLocationActuator actuator) {
if(!actuators.contains(actuator) && actuator instanceof SimUAVLocationActuator)
actuators.add((SimUAVLocationActuator) actuator);
}
/**
* Add an edge to the graph by specifying the
* start and end vertex.
*/
public E addEdge(V v1, V v2) {
E e = getEdgeFactory().createEdge(v1, v2);
addEdge(v1, v2, e);
@Override
public void placeComponent(SimLocationActuator actuator) {
//
}
return e;
@Override
public void setTimeBetweenMoveOperations(long time) {
this.timeBetweenMoveOperation = time;
}
/**
* Add an edge to the graph by specifying the
* start and end vertex as well as the edge
* that is connecting the two.
*/
public boolean addEdge(V v1, V v2, E e) {
edgeMap.put(e, null);
@Override
public void eventOccurred(Object content, int type) {
this.triggerComponentMovement();
}
return super.addEdge(v1, v2, e);
private void triggerComponentMovement() {
for (SimUAVLocationActuator actuator : actuators) {
actuator.getUAVMovement().move(timeBetweenMoveOperation);
}
/**
* Returns the set of all edges in the graph.
*
* @return
*/
public Set<E> getAllEdges() {
return edgeMap.keySet();
Event.scheduleWithDelay(timeBetweenMoveOperation, this, null, 1);
}
}
......@@ -24,7 +24,7 @@ import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.api.topology.movement.local.LocalMovementStrategy;
import de.tud.kom.p2psim.api.topology.obstacles.ObstacleModel;
import de.tud.kom.p2psim.api.topology.waypoints.WaypointModel;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.util.Either;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.route.RouteSensor;
......
......@@ -21,7 +21,7 @@
package de.tud.kom.p2psim.impl.topology.movement.local;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.util.Either;
import de.tud.kom.p2psim.impl.util.Left;
......
......@@ -37,8 +37,8 @@ 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.util.PositionVector;
import de.tud.kom.p2psim.impl.util.Either;
import de.tud.kom.p2psim.impl.util.Left;
import de.tudarmstadt.maki.simonstrator.api.Binder;
......
......@@ -26,35 +26,44 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import com.graphhopper.GHRequest;
import com.graphhopper.GHResponse;
import com.graphhopper.GraphHopper;
import com.graphhopper.PathWrapper;
import com.graphhopper.reader.osm.GraphHopperOSM;
import com.graphhopper.routing.util.EdgeFilter;
import com.graphhopper.routing.util.EncodingManager;
import com.graphhopper.routing.util.TraversalMode;
import com.graphhopper.routing.weighting.BlockAreaWeighting;
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.Parameters;
import com.graphhopper.util.Parameters.Routing;
import com.graphhopper.util.PointList;
import com.graphhopper.util.shapes.GHPoint;
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.movement.modularosm.ModularMovementModelViz;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.RouteSensorComponent;
import de.tud.kom.p2psim.impl.topology.movement.modularosm.attraction.RandomAttractionGenerator;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
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.Randoms;
import de.tudarmstadt.maki.simonstrator.api.Monitor.Level;
import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableException;
import de.tudarmstadt.maki.simonstrator.api.util.XMLConfigurableConstructor;
/**
* This movement strategy uses the data from osm and navigates the nodes throught streets to the destination
......@@ -64,9 +73,20 @@ import de.tudarmstadt.maki.simonstrator.api.component.ComponentNotAvailableExcep
*
* @author Martin Hellwig
* @version 1.0, 07.07.2015
*
* - added a possibility to allow the usage of alternative routes
* - set the probability for this alternatives
* - allow a dynamic reload of graphhopper files when switching routing algorithms
* - allow to define blocked areas, that are not used for routing
*
* @author Julian Zobel
* @version 02.04.2020
*
*/
public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
private Random random = Randoms.getRandom(RealWorldStreetsMovement.class);
private static PositionVector worldDimensions;
private GraphHopper hopper;
private LocationIndex index;
......@@ -81,18 +101,28 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
private String movementType; //car, bike or foot
private String defaultMovement;
private String navigationalType; //fastest,
private String blockedAreas; // default null
private static double latLower; //Values from -90 to 90; always smaller than latUpper
private static double latUpper; //Values from -90 to 90
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 allowAlternativeRoutes = false;
private double probabilityForAlternativeRoute = 0.0;
/**
* Tolerance in meters (if the node reached a waypoint up to "tolerance"
* meters, it will select the next waypoint in the path.
*/
private double tolerance = 1;
@XMLConfigurableConstructor({"blockedAreas"})
public RealWorldStreetsMovement(String blockedAreas) {
this();
this.setBlockedAreas(blockedAreas);
}
public RealWorldStreetsMovement() {
worldDimensions = Binder.getComponentOrNull(Topology.class)
.getWorldDimensions();
......@@ -100,12 +130,11 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
latUpper = GPSCalculation.getLatUpper();
lonLeft = GPSCalculation.getLonLeft();
lonRight = GPSCalculation.getLonRight();
}
private void init() {
hopper = new GraphHopper().forServer();
hopper.setOSMFile(osmFileLocation);
hopper = new GraphHopperOSM().forServer();
hopper.setDataReaderFile(osmFileLocation);
// where to store graphhopper files?
if (uniqueFolders) {
......@@ -118,10 +147,25 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
hopper.setGraphHopperLocation(graphFolderFiles + "/"
+ osmFileLocation.hashCode() + movementType);
}
hopper.setEncodingManager(new EncodingManager(movementType));
hopper.setEncodingManager(EncodingManager.create(movementType));
// if we have blocked areas defined, the CH in Graphhopper needs to be disabled!
// this also requires a new import
if((blockedAreas != null && !blockedAreas.isEmpty()) || allowAlternativeRoutes) {
hopper.setCHEnabled(false);
}
// try to load a cached import. If the cached import was with a different CH_ENABLED setting then now specified, this will fail.
// in this case, clean cache and re-import.
try {
hopper.importOrLoad();
index = hopper.getLocationIndex();
} catch (Exception e) {
System.err.println("[RealWorldStreetMovement] GraphHopper graph file must be imported again!");
hopper.clean();
hopper.importOrLoad();
}
index = hopper.getLocationIndex();
init = true;
}
......@@ -168,7 +212,22 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
setWeighting(navigationalType).
setVehicle(movementType).
setLocale(Locale.GERMANY);
// blocks one or multiple given areas from being used by the routing
// https://github.com/graphhopper/graphhopper/blob/master/docs/web/api-doc.md#flexible
if(blockedAreas != null && !blockedAreas.isEmpty()) {
req.getHints().put(Routing.BLOCK_AREA, blockedAreas);
}
if(allowAlternativeRoutes) {
// see: https://discuss.graphhopper.com/t/alternative-routes/424/11
req.setAlgorithm(Parameters.Algorithms.ALT_ROUTE);
req.getHints().put(Parameters.Algorithms.AltRoute.MAX_WEIGHT, "2.0"); // how much longer can alternatives be?
req.getHints().put(Parameters.Algorithms.AltRoute.MAX_PATHS, "5"); // how much alternatives should be returned at max?
}
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()) {
Monitor.log(this.getClass(), Monitor.Level.ERROR, "Routing request for Host %s with starting point (" +
......@@ -181,8 +240,21 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
currentRoutes.put(comp, trajectory);
} else {
PointList pointList = rsp.getBest().getPoints();
// alternatives for routing are allowed for distances bigger than 50
if(allowAlternativeRoutes && rsp.hasAlternatives() && rsp.getBest().getDistance() > 50) {
// alternative route is taken with a certain chance
if(random.nextDouble() <= probabilityForAlternativeRoute) {
List<PathWrapper> paths = rsp.getAll();
int pick = random.nextInt(paths.size() - 1) + 1;
pointList = rsp.getAll().get(pick).getPoints();
}
}
//PointList pointList = rsp.getBest().getPoints();
if (isCalculateRouteSegments()) {
/*
* Obtain segment IDs along the route.
......@@ -224,7 +296,8 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
PointList points = instr.getPoints();
String name = instr.getName();
if (name.isEmpty()) {
name = Integer.toString(instr.getPoints().toGHPoint(0).hashCode());
name = Integer.toString(instr.getPoints().get(0).hashCode());
//name = Integer.toString(instr.getPoints().toGHPoint(0).hashCode()); @deprecated for old GraphHopper version
}
for (GHPoint point : points) {
if (segment == null) {
......@@ -399,4 +472,19 @@ public class RealWorldStreetsMovement extends AbstractLocalMovementStrategy {
public void setCreateUniqueFolders(boolean uniqueFolders) {
this.uniqueFolders = uniqueFolders;
}
public void setBlockedAreas(String blockedAreas) {
if(!blockedAreas.isEmpty()) {
this.blockedAreas = blockedAreas;
}
}
public void setAllowAlternativeRoutes(boolean allowAlternativeRoutes) {
this.allowAlternativeRoutes = allowAlternativeRoutes;
}
public void setProbabilityForAlternativeRoute(double p) {
this.probabilityForAlternativeRoute = p;
}
}
......@@ -29,7 +29,7 @@ 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.tud.kom.p2psim.impl.topology.util.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;
......
......@@ -23,7 +23,7 @@ package de.tud.kom.p2psim.impl.topology.movement.local;
import java.util.List;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.topology.waypoints.graph.Waypoint;
import de.tud.kom.p2psim.impl.topology.waypoints.graph.WeakWaypoint;
......
......@@ -24,9 +24,11 @@ import java.util.ArrayList;
import java.util.List;
import java.util.WeakHashMap;
import org.jgrapht.graph.DefaultWeightedEdge;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tud.kom.p2psim.impl.topology.PositionVector;
import de.tud.kom.p2psim.impl.topology.waypoints.graph.Path;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.topology.waypoints.graph.PathEdge;
import de.tud.kom.p2psim.impl.topology.waypoints.graph.Waypoint;
import de.tud.kom.p2psim.impl.topology.waypoints.graph.WeakWaypoint;
import de.tud.kom.p2psim.impl.util.Either;
......@@ -114,7 +116,7 @@ public class ShortestPathWaypointMovement extends AbstractLocalMovementStrategy
Waypoint closestWaypointToDestination = waypointModel
.getClosestWaypoint(finalDestination, WeakWaypoint.class);
List<Path> shortestPath = waypointModel.getShortestPath(
List<PathEdge> shortestPath = waypointModel.getShortestPath(
closestWaypointToCurrentPosition, closestWaypointToDestination);
List<Waypoint> waypointList = buildWaypointList(
......@@ -133,13 +135,13 @@ public class ShortestPathWaypointMovement extends AbstractLocalMovementStrategy
* @return
*/
protected List<Waypoint> buildWaypointList(Waypoint start,
List<Path> shortestPath) {
List<PathEdge> shortestPath) {
List<Waypoint> waypointList = new ArrayList<Waypoint>();
Waypoint lastWaypoint = start;
waypointList.add(start);
for (Path p : shortestPath) {
for (PathEdge p : shortestPath) {
lastWaypoint = p.getOtherEnd(lastWaypoint);
waypointList.add(lastWaypoint);
}
......
......@@ -21,6 +21,7 @@
package de.tud.kom.p2psim.impl.topology.movement.modularosm;
import de.tud.kom.p2psim.api.topology.Topology;
import de.tud.kom.p2psim.impl.topology.util.PositionVector;
import de.tud.kom.p2psim.impl.topology.views.VisualizationTopologyView.VisualizationInjector;
import de.tudarmstadt.maki.simonstrator.api.Binder;
......@@ -135,6 +136,34 @@ public class GPSCalculation {
this.lonCenter = lonCenter;
}
/**
* 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
*/
public static PositionVector transformGPSWindowToOwnWorld(double lat, double lon) {
double x = getWorldDimensionsX() * (lon - getLonLeft())/(getLonRight() - getLonLeft());
//Invert the y value, because in Java Swing we start drawing in the upper left corner instead in the lower left one
double y = getWorldDimensionsY() - getWorldDimensionsY() * (lat - getLatLower())/(getLatUpper() - getLatLower());
return new PositionVector(x, y);
}
/**
* Checks if the given latitute and longitude is within the simulated GPS area.
*
* @param lat
* @param lon
* @return True if within the GPS area, false otherwise.
*/
public static boolean isWithinGPSBoundaries(double lat, double lon) {
if(lat > getLatLower() && lat < getLatUpper() &&
lon > getLonLeft() && lon < getLonRight()) {
return true;
}
else return false;
}
/**
* Formula http://wiki.openstreetmap.org/wiki/Zoom_levels
*
......
......@@ -18,53 +18,25 @@
*
*/
package de.tud.kom.p2psim.impl.topology.waypoints.graph;
package de.tud.kom.p2psim.impl.topology.movement.modularosm;
import java.util.Set;
import java.util.WeakHashMap;
import org.jgrapht.EdgeFactory;
import org.jgrapht.graph.DefaultDirectedWeightedGraph;
import de.tud.kom.p2psim.api.topology.movement.SimLocationActuator;
import de.tudarmstadt.maki.simonstrator.api.component.core.MonitorComponent.Analyzer;
import de.tudarmstadt.maki.simonstrator.api.component.sensor.location.IAttractionPoint;
/**
* The simple weighted edge retrievable graph caches the edges
* for later batch retrieval.
*
* It uses the WeakHashMap to avoid unintentional references to
* dead objects.
*
* @author Fabio Zöllner
* @version 1.0, 27.03.2012
* @param <V> Vertices
* @param <E> Edges
*/
public class DefaultDirectedWeightedEdgeRetrievableGraph<V, E> extends DefaultDirectedWeightedGraph<V, E> {
private WeakHashMap<E, Object> edgeMap = new WeakHashMap<E, Object>();
public interface IAttractionBasedMovementAnalyzer extends Analyzer {
@SuppressWarnings("unchecked")
public DefaultDirectedWeightedEdgeRetrievableGraph(EdgeFactory edgefactory) {
super(edgefactory);
}
public void onAllNodeInitializationCompleted(Set<SimLocationActuator> nodes);
@SuppressWarnings("unchecked")
public DefaultDirectedWeightedEdgeRetrievableGraph(Class class1) {
super(class1);
}
public void onAttractionPointAdded(IAttractionPoint attractionpoint);
public E addEdge(V v1, V v2) {
E e = getEdgeFactory().createEdge(v1, v2);
addEdge(v1, v2, e);
public void onAttractionPointRemoved(IAttractionPoint attractionpoint);
return e;
}
public void onUpdateAttractionAssignment(SimLocationActuator node, IAttractionPoint attractionpoint);
public boolean addEdge(V v1, V v2, E e) {
edgeMap.put(e, null);
return super.addEdge(v1, v2, e);
}
public void onNodeReachedAttractionPoint(SimLocationActuator node, IAttractionPoint attractionpoint);
public Set<E> getAllEdges() {
return edgeMap.keySet();
}
}
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