diff --git a/.gitignore b/.gitignore
index ae3c172..5025588 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,8 @@
/bin/
+/processeddata/
+/rawdata/
+/rome/
+sanfrancisco/
+/beijing/
+/ARCHIVES/
+.idea
diff --git a/Sim2Car.iml b/Sim2Car.iml
new file mode 100644
index 0000000..ac38aac
--- /dev/null
+++ b/Sim2Car.iml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/application/ApplicationType.java b/src/application/ApplicationType.java
index 32cc6ac..fe1f48e 100644
--- a/src/application/ApplicationType.java
+++ b/src/application/ApplicationType.java
@@ -8,5 +8,7 @@ public enum ApplicationType {
ROUTING_APP, // the smart Navigator application
STREET_VISITS_APP, // application that records the streets visit count
TRAFFIC_LIGHT_CONTROL_APP,
- SYNCHRONIZE_INTERSECTIONS_APP // app that syncronize traffic lights in multiple intersections
+ SYNCHRONIZE_INTERSECTIONS_APP, // app that syncronize traffic lights in multiple intersections
+ TRAFFIC_LIGHT_ROUTING_APP,
+ CAR_ROUTING_APP
}
diff --git a/src/application/ApplicationUtils.java b/src/application/ApplicationUtils.java
index 59e5297..ac9e17a 100644
--- a/src/application/ApplicationUtils.java
+++ b/src/application/ApplicationUtils.java
@@ -2,6 +2,8 @@
import java.util.Vector;
+import application.dynamic_routing.CarDynamicRoutingApp;
+import application.dynamic_routing.TrafficLightDynamicRoutingApp;
import controller.engine.EngineSimulation;
import controller.newengine.SimulationEngine;
import application.multipleIntersections.SynchronizeIntersectionsApplication;
@@ -38,6 +40,9 @@ public static Application activateApplicationCar( ApplicationType type, GeoCar c
return EngineSimulation.getInstance() != null ? new TileApplicationCar(car) : null;
case STREET_VISITS_APP:
return SimulationEngine.getInstance() != null ? new StreetVisitsApplication(car) : null;
+
+ case CAR_ROUTING_APP:
+ return SimulationEngine.getInstance() != null ? new CarDynamicRoutingApp(true, car, ApplicationType.CAR_ROUTING_APP) : null;
default:
return null;
}
@@ -54,6 +59,9 @@ public static Application activateApplicationTrafficLight( ApplicationType type,
switch (type) {
case TRAFFIC_LIGHT_CONTROL_APP:
return SimulationEngine.getInstance() != null ? new ApplicationTrafficLightControl(trafficLight) : null;
+
+ case TRAFFIC_LIGHT_ROUTING_APP:
+ return SimulationEngine.getInstance() != null ? new TrafficLightDynamicRoutingApp(true, trafficLight) : null;
default:
return null;
}
@@ -94,7 +102,7 @@ public static Application activateApplicationServer( ApplicationType type, GeoSe
}
/**
- * @param netInterfaces - String with active Applications
+ * @param //(netInterfaces) - String with active Applications
*/
public static void parseApplications(String activeApplications) {
String activeApps[] = activeApplications.split(",");
diff --git a/src/application/dynamic_routing/CarDynamicRoutingApp.java b/src/application/dynamic_routing/CarDynamicRoutingApp.java
new file mode 100644
index 0000000..43b7758
--- /dev/null
+++ b/src/application/dynamic_routing/CarDynamicRoutingApp.java
@@ -0,0 +1,182 @@
+package application.dynamic_routing;
+
+import application.Application;
+import application.ApplicationType;
+import controller.network.NetworkInterface;
+import controller.network.NetworkType;
+import model.GeoCar;
+import model.mobility.MobilityEngine;
+import model.network.Message;
+import model.network.MessageType;
+import model.parameters.Globals;
+import application.streetCostSharing.StreetsCostSharing;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+
+public class CarDynamicRoutingApp extends Application {
+
+ private boolean isActive;
+ private GeoCar car;
+ private ApplicationType type;
+ private MobilityEngine mobilityEngine;
+
+ public CarDynamicRoutingApp(boolean isActive, GeoCar car, ApplicationType type) {
+ this.isActive = isActive;
+ this.car = car;
+ this.type = type;
+ this.mobilityEngine = MobilityEngine.getInstance();
+ }
+
+ @Override
+ public boolean getStatus() {
+ return false;
+ }
+
+ @Override
+ public String run() {
+ NetworkInterface net = car.getNetworkInterface(NetworkType.Net_WiFi);
+ net.processOutputQueue();
+ return "";
+ }
+
+ @Override
+ public String stop() {
+ return null;
+ }
+
+ @Override
+ public String getInfoApp() {
+ return null;
+ }
+
+ @Override
+ public Object getData() {
+ return null;
+ }
+
+ @Override
+ public ApplicationType getType() {
+ return type;
+ }
+
+
+ public void sendOutdatedDataToTrafficLight(HashMap outdated, long destinationTrafficLightId) {
+ NetworkInterface networkInterface = this.car.getNetworkInterface(NetworkType.Net_WiFi);
+ Message message = new Message(this.car.getId(), destinationTrafficLightId, outdated.clone(),
+ MessageType.OUTDATED_COSTS, ApplicationType.TRAFFIC_LIGHT_ROUTING_APP);
+ networkInterface.putMessage(message);
+ }
+
+
+ public void sendOutdatedDataToCar(HashMap outdated, long neighbourCarId) {
+ NetworkInterface networkInterface = this.car.getNetworkInterface(NetworkType.Net_WiFi);
+ Message message = new Message(this.car.getId(), neighbourCarId, outdated.clone(),
+ MessageType.OUTDATED_COSTS, ApplicationType.CAR_ROUTING_APP);
+ networkInterface.putMessage(message);
+ }
+
+
+ public boolean isOutdated(double oldCost, double updateCost, long currentWayId, VotingStreetCostData votingStreetCostData) {
+ boolean returnValue = true;
+
+ /* if the cost itself is new for the car's KB*/
+ if (Math.abs(oldCost - updateCost) >
+ (oldCost * StreetsCostSharing.STREET_COST_UPDATE_THRESHOLD)) {
+ returnValue = false;
+ }
+
+ /*if the cost does not have any new stamp cases:
+ * has new stamps -> not outdated
+ * does not have -> is outdated
+ * it is a new cost to share -> not outdated*/
+
+ if (returnValue && Globals.useVotingSystem) {
+ if (this.car.getStreetsCostSharing().getStreetUpdates().containsKey(currentWayId)) {
+ boolean hasNewStamps = this.car.getStreetsCostSharing().getStreetUpdates()
+ .get(currentWayId).hasNewStamps(votingStreetCostData);
+ returnValue = !hasNewStamps;
+ } else {
+ returnValue = false;
+ }
+ }
+
+ return returnValue;
+ }
+
+
+ /* this function has the role to iterate through the reported costs and to
+ * split them into valid data or outdated data (outdated data is useful only
+ * in CARS communication context)*/
+ private ArrayList> updatesPreProcessing(Message m) {
+ HashMap updates;
+ HashMap validUpdates = new HashMap();
+ HashMap outdatedUpdates = new HashMap();
+ Iterator itr;
+
+ updates = (HashMap) m.getData();
+ itr = updates.keySet().iterator();
+ long currentWayId;
+ double oldCost, updateCost;
+
+ while (itr.hasNext()) {
+ currentWayId = itr.next();
+ oldCost = car.getStreetsCostSharing().getWayCost(mobilityEngine.getWay(currentWayId));
+ updateCost = updates.get(currentWayId).streetCost;
+
+ /*check outdated*/
+ if (!isOutdated(oldCost, updateCost, currentWayId, updates.get(currentWayId))) {
+
+ car.getStreetsCostSharing().updateWayCost(currentWayId, updates.get(currentWayId));
+ validUpdates.put(currentWayId, updates.get(currentWayId));
+
+ } else {
+ outdatedUpdates.put(currentWayId, updates.get(currentWayId));
+ }
+ }
+ ArrayList> aux = new ArrayList>();
+ aux.add(validUpdates);
+ aux.add(outdatedUpdates);
+ return aux;
+ }
+
+
+ @Override
+ public void process(Message m) {
+ ArrayList> aux;
+ HashMap outdated;
+
+ switch (m.getType()) {
+ case TRAFFIC_LIGHT_INFORMS:
+
+ aux = updatesPreProcessing(m);
+ outdated = aux.get(1);
+
+ /*if there is some outdated data, inform the traffic light*/
+ if (!outdated.isEmpty()) {
+ sendOutdatedDataToTrafficLight(outdated, m.getSourceId());
+ }
+
+ break;
+
+ case CAR_INFORMS:
+ /*some other car informs current car*/
+
+ aux = updatesPreProcessing(m);
+ outdated = aux.get(1);
+
+ if (!outdated.isEmpty()) {
+ sendOutdatedDataToCar(outdated, m.getSourceId());
+ }
+ break;
+
+ case OUTDATED_COSTS:
+
+ HashMap outdatedUpdates = (HashMap) m.getData();
+ this.car.getStreetsCostSharing().removeOutdatedInfo(outdatedUpdates);
+
+ break;
+ }
+ }
+}
diff --git a/src/application/dynamic_routing/TrafficLightDynamicRoutingApp.java b/src/application/dynamic_routing/TrafficLightDynamicRoutingApp.java
new file mode 100644
index 0000000..b50804c
--- /dev/null
+++ b/src/application/dynamic_routing/TrafficLightDynamicRoutingApp.java
@@ -0,0 +1,276 @@
+package application.dynamic_routing;
+
+import application.Application;
+import application.ApplicationType;
+import controller.network.NetworkInterface;
+import controller.network.NetworkType;
+import controller.network.NetworkWiFi;
+import model.GeoTrafficLightMaster;
+import model.OSMgraph.Node;
+import model.OSMgraph.Way;
+import model.mobility.MobilityEngine;
+import model.network.Message;
+import model.network.MessageType;
+import application.streetCostSharing.StreetsCostSharing;
+import utils.tracestool.Utils;
+
+
+import java.util.*;
+
+public class TrafficLightDynamicRoutingApp extends Application {
+ private boolean isActive;
+ private GeoTrafficLightMaster trafficLightMaster;
+ private ApplicationType type;
+ private MobilityEngine mobilityEngine;
+ private HashMap streetGraphCost;
+ private HashMap streetUpdatesToCars;
+ private HashMap closestTrafficLightsMap;
+ private NetworkWiFi networkInterface;
+
+ public static final int NORTH_INDEX = 0;
+ public static final int SOUTH_INDEX = 1;
+ public static final int EAST_INDEX = 2;
+ public static final int WEST_INDEX = 3;
+
+
+
+ public TrafficLightDynamicRoutingApp(boolean isActive, GeoTrafficLightMaster trafficLightMaster) {
+ this.isActive = isActive;
+ this.trafficLightMaster = trafficLightMaster;
+ mobilityEngine = MobilityEngine.getInstance();
+ type = ApplicationType.TRAFFIC_LIGHT_ROUTING_APP;
+ streetGraphCost = new HashMap();
+
+ /*this structure has the role to keep track of the latest updates*/
+ streetUpdatesToCars = new HashMap();
+ }
+
+ public void postConstructInit() {
+ networkInterface = (NetworkWiFi) trafficLightMaster.getNetworkInterface(NetworkType.Net_WiFi);
+ closestTrafficLightsMap = networkInterface.discoverClosestTrafficLightsGeographic();
+ }
+
+
+ public double getWayCost(Way way) {
+ double streetCost;
+
+ if (!streetGraphCost.containsKey(way.id)) {
+ Node firstNode = way.nodes.firstElement();
+ Node lastNode = way.nodes.lastElement();
+
+ double streetLength = Utils.getRealDistanceAB(way, firstNode.id, lastNode.id);
+ streetCost = streetLength / (way.getMaximumSpeed() - 3);
+ streetGraphCost.put(way.id, streetCost);
+
+ } else {
+ streetCost = streetGraphCost.get(way.id);
+ }
+ return streetCost;
+ }
+
+ @Override
+ public boolean getStatus() {
+ return isActive;
+ }
+
+// this function is called periodically from Simulation Engine (to send the messages from outputQueue)
+ @Override
+ public String run() {
+ NetworkInterface net = trafficLightMaster.getNetworkInterface(NetworkType.Net_WiFi);
+ net.processOutputQueue();
+ return "";
+ }
+
+ @Override
+ public String stop() {
+ return null;
+ }
+
+ @Override
+ public String getInfoApp() {
+ return null;
+ }
+
+ @Override
+ public Object getData() {
+ return null;
+ }
+
+ @Override
+ public ApplicationType getType() {
+ return type;
+ }
+
+ private void sendUpdatesToCar(long destinationCarId) {
+ Message message = new Message(this.trafficLightMaster.getId(), destinationCarId, this.streetUpdatesToCars.clone(),
+ MessageType.TRAFFIC_LIGHT_INFORMS, ApplicationType.CAR_ROUTING_APP);
+
+ this.networkInterface.putMessage(message);
+ }
+
+
+ private void spreadUpdatesToNeighbours(HashMap validUpdates, long sourceId){
+
+ Iterator it = closestTrafficLightsMap.entrySet().iterator();
+ long neighbourTrafficLightId;
+ int neighbourDirection;
+ Random rand = new Random();
+ int stepOver1 = rand.nextInt(closestTrafficLightsMap.size());
+ int stepOver2 = rand.nextInt(closestTrafficLightsMap.size());
+
+ if (stepOver1 == stepOver2) {
+ stepOver2 = (stepOver1 + 1) % closestTrafficLightsMap.size();
+ }
+
+ if (closestTrafficLightsMap.size() < 4) {
+ stepOver2 = -1;
+ }
+
+ int i = -1;
+ int sents = 0;
+ while (it.hasNext()) {
+ i++;
+ Map.Entry pair = (Map.Entry) it.next();
+ neighbourDirection = (int) pair.getKey();
+ neighbourTrafficLightId = ((NetworkInterface) pair.getValue()).getOwner().getId();
+
+ if (neighbourTrafficLightId == sourceId) {
+ continue;
+ }
+
+ if (i == stepOver1 || i == stepOver2) {
+ continue;
+ }
+
+ /** to reduce the cpu consumption uncomment this, and comment the ones above*/
+// if (i != stepOver1) continue;
+
+ Message message = new Message(this.trafficLightMaster.getId(), neighbourTrafficLightId,
+ validUpdates.clone(), MessageType.TRAFFIC_LIGHT_INFORMS, ApplicationType.TRAFFIC_LIGHT_ROUTING_APP);
+ message.setMessageDirection(neighbourDirection);
+ sents++;
+
+ this.networkInterface.putMessage(message);
+
+ }
+ }
+
+ private void sendBackOutdatedUpdates(HashMap outdatedUpdates,
+ long destinationId) {
+ Message message = new Message(this.trafficLightMaster.getId(), destinationId, outdatedUpdates.clone(),
+ MessageType.OUTDATED_COSTS, ApplicationType.CAR_ROUTING_APP);
+ this.networkInterface.putMessage(message);
+ }
+
+
+ /* this function has the role to iterate through the reported costs and to
+ * split them into valid data or outdated data (outdated data is useful only
+ * in CARS communication context*/
+ private ArrayList> updatesPreProcessing(Message m) {
+ HashMap updates;
+ HashMap validUpdates = new HashMap();
+ HashMap outdatedUpdates = new HashMap();
+ Iterator itr;
+
+ updates = (HashMap) m.getData();
+ itr = updates.keySet().iterator();
+ long currentWayId;
+ double oldCost, updateCost;
+
+ while (itr.hasNext()) {
+ currentWayId = itr.next();
+ oldCost = getWayCost(mobilityEngine.getWay(currentWayId));
+ updateCost = updates.get(currentWayId).getStreetCost();
+
+ /*check if that cost is new and update, otherwise report it as outdated*/
+ if (Math.abs(oldCost - updateCost) >
+ (oldCost * StreetsCostSharing.STREET_COST_UPDATE_THRESHOLD)) {
+ streetGraphCost.put(currentWayId, updateCost);
+ validUpdates.put(currentWayId, updates.get(currentWayId));
+
+ /*put the news in the structure responsible for sharing with the cars*/
+ streetUpdatesToCars.put(currentWayId, updates.get(currentWayId));
+ } else {
+ /* inform the car that this message is outdated, so put the data into a map to be sent*/
+ if (m.getType() == MessageType.CAR_INFORMS)
+ outdatedUpdates.put(currentWayId, updates.get(currentWayId));
+ }
+ }
+ ArrayList> aux = new ArrayList>();
+ aux.add(validUpdates);
+ aux.add(outdatedUpdates);
+ return aux;
+ }
+
+
+ @Override
+ public void process(Message m) {
+ HashMap validUpdates;
+ HashMap outdatedUpdates;
+ ArrayList> aux;
+ Iterator itr;
+
+ switch (m.getType()) {
+ case CAR_INFORMS:
+
+ /* send back some updates*/
+ if (!this.streetUpdatesToCars.isEmpty()) {
+ sendUpdatesToCar(m.getSourceId());
+ }
+
+ aux = updatesPreProcessing(m);
+ validUpdates = aux.get(0);
+ outdatedUpdates = aux.get(1);
+
+ /* spread this update to the neighbours (traffic lights) and inform the car about outdated data*/
+ if (!validUpdates.isEmpty()) {
+ spreadUpdatesToNeighbours(validUpdates, m.getSourceId());
+ }
+
+ if (!outdatedUpdates.isEmpty()) {
+ sendBackOutdatedUpdates(outdatedUpdates, m.getSourceId());
+ }
+ break;
+
+ case TRAFFIC_LIGHT_INFORMS:
+
+ aux = updatesPreProcessing(m);
+ validUpdates = aux.get(0);
+
+ /*it's not necessary to send outdated data because the traffic lights spread
+ * the message only to the closest neighbours. If the message is outdated,
+ * the current traffic light does not spread it anymore...
+ * */
+ if (!validUpdates.isEmpty()) {
+ spreadUpdatesToNeighbours(validUpdates, m.getSourceId());
+ }
+
+ break;
+
+ /* this type of message is only received from cars*/
+ case OUTDATED_COSTS:
+
+ /* remove outdated updates*/
+ outdatedUpdates = (HashMap) m.getData();
+ itr = outdatedUpdates.keySet().iterator();
+ long currentWayId;
+ double currentUpdateCost;
+ double outdatedCost;
+
+ while (itr.hasNext()) {
+ currentWayId = itr.next();
+ currentUpdateCost = streetUpdatesToCars.get(currentWayId).getStreetCost();
+ outdatedCost = outdatedUpdates.get(currentWayId).getStreetCost();
+
+ /*check if the currentUpdateCost has been modified before receiving this update
+ * regarding outdated cost. So, if the difference between this 2 costs is smaller than THRESHOLD,
+ * there has been an update over this cost so it is not outdated anymore*/
+ if (Math.abs(currentUpdateCost - outdatedCost) <
+ (currentUpdateCost * StreetsCostSharing.STREET_COST_UPDATE_THRESHOLD)) {
+ streetUpdatesToCars.remove(currentWayId);
+ }
+ }
+ break;
+ }
+ }
+}
diff --git a/src/application/dynamic_routing/VotingStreetCostData.java b/src/application/dynamic_routing/VotingStreetCostData.java
new file mode 100644
index 0000000..79731c0
--- /dev/null
+++ b/src/application/dynamic_routing/VotingStreetCostData.java
@@ -0,0 +1,228 @@
+package application.dynamic_routing;
+
+import application.streetCostSharing.StreetsCostSharing;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+
+
+public class VotingStreetCostData {
+ double streetCost;
+ boolean firstVotingSession;
+
+ /*each cost should be stamped by multiple entities before taking it into consideration.
+ * there could be multiple costs reported for the same street, so we need
+ * to keep track of each cost ant its stamps in order to make the best updated decision*/
+ HashMap> costVotingMap;
+
+ public int DYNAMIC_VOTING_THRESHOLD = 2;
+
+ public HashMap> getCostVotingMap() {
+ return costVotingMap;
+ }
+
+ public void setCostVotingMap(HashMap> costVotingMap) {
+ this.costVotingMap = costVotingMap;
+ }
+
+ public VotingStreetCostData(double cost, long discovererStamp) {
+ this.costVotingMap = new HashMap>();
+ HashSet stamps = new HashSet();
+ stamps.add(discovererStamp);
+ this.costVotingMap.put(cost, stamps);
+ this.streetCost = cost;
+ this.firstVotingSession = true;
+ }
+
+ public double getStreetCost() {
+ return streetCost;
+ }
+
+ public void setStreetCost(double streetCost) {
+ this.streetCost = streetCost;
+ }
+
+ /** make sure to add a stamp to the correct cost
+ * if a similar cost is not found, create another slot*/
+ public void addDiscovererStamp(double cost, Long stamp) {
+ HashSet stamps;
+ Iterator it = costVotingMap.keySet().iterator();
+
+ while(it.hasNext()) {
+ double currentCost = it.next();
+
+ if (VotingStreetCostData.isTheSameStreetCost(currentCost, cost)) {
+ stamps = costVotingMap.get(currentCost);
+ stamps.add(stamp);
+ return;
+ }
+ }
+ stamps = new HashSet();
+ stamps.add(stamp);
+ costVotingMap.put(cost, stamps);
+ }
+
+ public boolean isValidated(double cost, int votingThreshold) {
+
+ if (costVotingMap.get(cost).size() >= votingThreshold) {
+ return true;
+ }
+ return false;
+ }
+
+
+ public static boolean isTheSameStreetCost(double c1, double c2) {
+ if (Math.abs(c1 - c2) <
+ (c1 * StreetsCostSharing.STREET_COST_UPDATE_THRESHOLD)) {
+ return true;
+ }
+ return false;
+ }
+
+
+ /** this method is useful in CarDynamicRoutingApp, at outdated checking. It returns
+ * if there are new stamps in the param object for a specific cost*/
+ public boolean hasNewStamps(VotingStreetCostData votingStreetCostData) {
+ double paramCost = votingStreetCostData.getStreetCost();
+ double costReference = -1;
+ boolean hasTheCost = false;
+ Iterator costIt = this.costVotingMap.keySet().iterator();
+
+ /*check if the cost exists*/
+ while (costIt.hasNext()) {
+ double currentCost = costIt.next();
+
+ if (VotingStreetCostData.isTheSameStreetCost(currentCost, paramCost)) {
+ hasTheCost = true;
+ /*keep the value of the current cost to refer the cost in the current Object
+ * it could be slightly different*/
+ costReference = currentCost;
+ break;
+ }
+ }
+
+ if (!hasTheCost) return false;
+
+ Iterator it = votingStreetCostData.getCostVotingMap().get(paramCost).iterator();
+ long currentStamp;
+
+ /*check each stamp*/
+ while (it.hasNext()) {
+ currentStamp = it.next();
+
+ if (!this.costVotingMap.get(costReference).contains(currentStamp)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /** add new costs with stamps, or add new stamps to the existing costs*/
+ public void addNewStamps(VotingStreetCostData votingStreetCostData) {
+ Iterator costIterator = votingStreetCostData.getCostVotingMap().keySet().iterator();
+ Iterator currentSessionCostIterator;
+
+ while (costIterator.hasNext()) {
+ double currentCost = costIterator.next();
+ currentSessionCostIterator = this.costVotingMap.keySet().iterator();
+
+ Iterator stampIterator = votingStreetCostData.getCostVotingMap().get(currentCost).iterator();
+ long currentStamp;
+ HashSet added = new HashSet();
+
+ /*if the cost is not present into the Map, add the cost and its stamps from the param object*/
+ while (currentSessionCostIterator.hasNext()) {
+ double votedCost = currentSessionCostIterator.next();
+ if (VotingStreetCostData.isTheSameStreetCost(votedCost, currentCost)) {
+ added.add(currentCost);
+
+ /*iterate through stamps and add the new ones*/
+ while (stampIterator.hasNext()) {
+ currentStamp = stampIterator.next();
+
+ if (!this.costVotingMap.get(votedCost).contains(currentStamp)) {
+ this.costVotingMap.get(votedCost).add(currentStamp);
+ }
+ }
+ }
+ }
+ if (added.contains(currentCost)) continue;
+ /*if the cost not found in the session, add all stamps*/
+ costVotingMap.put(currentCost, votingStreetCostData.getCostVotingMap().get(currentCost));
+ }
+ }
+
+ /* check if there is a cost in the voting session that is greater than the threshold
+ * there are 2 cases:
+ * - an increased report
+ * - a decreased report
+ * The threshold is dynamically changed depending on the report type*/
+ public boolean isStreetCostUpdated() {
+ Iterator costIterator = this.costVotingMap.keySet().iterator();
+
+ while (costIterator.hasNext()) {
+ double currentCost = costIterator.next();
+
+ if (currentCost == this.streetCost && !firstVotingSession) {
+ continue;
+ }
+
+ /*first voting session*/
+ if (this.firstVotingSession) {
+ if (this.costVotingMap.get(currentCost).size() >= DYNAMIC_VOTING_THRESHOLD) {
+ this.streetCost = currentCost;
+ firstVotingSession = false;
+ return true;
+ }
+
+ } else if (currentCost > streetCost) {
+ /*increased cost -> UPPER_THRESHOLD*/
+ if (this.costVotingMap.get(currentCost).size() >= DYNAMIC_VOTING_THRESHOLD) {
+ this.streetCost = currentCost;
+
+ /*if another session wants to increase the cost more, it has to overwhelm a bigger threshold*/
+ if (DYNAMIC_VOTING_THRESHOLD < StreetsCostSharing.MAX_DYNAMIC_VOTING_THRESHOLD)
+ DYNAMIC_VOTING_THRESHOLD += 1;
+ return true;
+ }
+ } else {
+ /*decreased cost -> LOWER_THRESHOLD*/
+ if (this.costVotingMap.get(currentCost).size() >= DYNAMIC_VOTING_THRESHOLD) {
+ this.streetCost = currentCost;
+
+ if (DYNAMIC_VOTING_THRESHOLD > StreetsCostSharing.MIN_DYNAMIC_VOTING_THRESHOLD)
+ DYNAMIC_VOTING_THRESHOLD -= 1;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /* after a decision is made, there is no point in keeping the costs
+ * that were not voted*/
+ public void clearVoteSession() {
+ this.costVotingMap.entrySet().removeIf(e -> e.getKey() != this.streetCost);
+ }
+
+
+ public void printVoteSession() {
+ Iterator costIterator = this.costVotingMap.keySet().iterator();
+ System.out.println("VOTE SESSION");
+ while (costIterator.hasNext()) {
+ double currentCost = costIterator.next();
+ System.out.println("[" + currentCost + "] " + this.costVotingMap.get(currentCost).toString());
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "VotingStreetCostData{" +
+ "streetCost=" + streetCost +
+ ", costVotingMap=" + costVotingMap.toString() +
+ ", UPPER_DYNAMIC_VOTING_THRESHOLD=" + DYNAMIC_VOTING_THRESHOLD +
+ '}';
+ }
+}
diff --git a/src/application/dynamic_routing/VotingStreetCostDataTest.java b/src/application/dynamic_routing/VotingStreetCostDataTest.java
new file mode 100644
index 0000000..e54c89a
--- /dev/null
+++ b/src/application/dynamic_routing/VotingStreetCostDataTest.java
@@ -0,0 +1,226 @@
+package application.dynamic_routing;
+
+
+import java.util.HashMap;
+
+public class VotingStreetCostDataTest {
+ VotingStreetCostData votingStreetCostData;
+ HashMap myMap = new HashMap();
+
+ public void idTheSameCostTest() {
+ System.out.println("---- IS THE SAME COST TEST.. ----");
+ // Considering Threshold = 0.2
+ if (VotingStreetCostData.isTheSameStreetCost(9, 10)) {
+ System.out.println("PASS");
+ } else {
+ System.out.println("FAIL");
+ }
+
+ if (VotingStreetCostData.isTheSameStreetCost(7, 10)) {
+ System.out.println("FAIL");
+ } else {
+ System.out.println("PASS");
+ }
+ System.out.println();
+ }
+
+ public void addNewCostsTest() {
+ System.out.println("---- ADD NEW COSTS TEST... ----");
+ System.out.println("ADDING 22 and 14.5");
+ votingStreetCostData = new VotingStreetCostData(22, 1);
+ votingStreetCostData.addDiscovererStamp(10, (long) 5);
+ votingStreetCostData.printVoteSession();
+ if (!votingStreetCostData.isStreetCostUpdated()) {
+ System.out.println("[PASS]: Not updated because there are not enough votes");
+ } else {
+ System.out.println("[FAIL]: at update 1");
+ }
+
+ System.out.println("ADDING 15");
+ votingStreetCostData.addDiscovererStamp(11, (long) 4);
+ votingStreetCostData.addDiscovererStamp(11, (long) 4);
+
+ if (votingStreetCostData.isStreetCostUpdated()) {
+ System.out.println("[PASS]: updated because there ARE enough votes");
+ } else {
+ System.out.println("[FAIL]: at update 2");
+ }
+
+ if (VotingStreetCostData.isTheSameStreetCost(votingStreetCostData.getStreetCost(), 11)) {
+ System.out.println("[PASS]: Current cost correct = " + votingStreetCostData.getStreetCost());
+ } else {
+ System.out.println("[Fail]: wrong cost " + votingStreetCostData.getStreetCost());
+ }
+
+ System.out.println("ADDING 21 and 23");
+ votingStreetCostData.addDiscovererStamp(21, (long) 2);
+ votingStreetCostData.addDiscovererStamp(23, (long) 3);
+
+
+ if (votingStreetCostData.isStreetCostUpdated()) {
+ System.out.println("[PASS]: updated because there ARE enough votes");
+ } else {
+ System.out.println("[FAIL]: at update 2");
+ }
+
+ if (VotingStreetCostData.isTheSameStreetCost(votingStreetCostData.getStreetCost(), 21)) {
+ System.out.println("[PASS]: Current cost correct = " + votingStreetCostData.getStreetCost());
+ } else {
+ System.out.println("[Fail]: wrong cost " + votingStreetCostData.getStreetCost());
+ }
+
+ if (votingStreetCostData.getCostVotingMap().size() == 2) {
+ System.out.println("[PASS]: Number of costs is correct");
+ } else {
+ System.out.println("[FAIL]: WRONG: " + votingStreetCostData.getCostVotingMap().size() + " costs instead of 2");
+ }
+ System.out.println();
+ System.out.println("SHOULD HAVE 3 VOTES for 22 AND 2 VOTES FOR 11");
+ votingStreetCostData.printVoteSession();
+ System.out.println();
+ this.votingStreetCostData.clearVoteSession();
+ }
+
+ public void hasNewStampsTest() {
+ System.out.println("---- HAS NEW STAMPS TEST... ----");
+ VotingStreetCostData votingStreetCostDataAux = new VotingStreetCostData(50, 10);
+
+ if (this.votingStreetCostData.hasNewStamps(votingStreetCostDataAux)) {
+ System.out.println("[FAIL]");
+ } else {
+ System.out.println("[PASS]");
+ }
+ votingStreetCostData.addDiscovererStamp(15, (long) 4);
+
+ votingStreetCostDataAux.addDiscovererStamp(14.5, (long) 14);
+ votingStreetCostDataAux.addDiscovererStamp(15, (long) 15);
+
+ /*update the the street cost after the last votes*/
+ votingStreetCostDataAux.isStreetCostUpdated();
+
+ if (this.votingStreetCostData.hasNewStamps(votingStreetCostDataAux)) {
+ System.out.println("[PASS]");
+ } else {
+ System.out.println("[FAIL]");
+ }
+ votingStreetCostData.clearVoteSession();
+ System.out.println();
+ }
+
+ public void addNewStampsTest() {
+ System.out.println("---- ADD NEW STAMPS TEST ...---");
+ votingStreetCostData.getCostVotingMap().clear();
+ this.votingStreetCostData.addDiscovererStamp(15, (long) 4);
+ this.votingStreetCostData.addDiscovererStamp(16, (long) 3);
+ this.votingStreetCostData.addDiscovererStamp(15, (long) 2);
+ this.votingStreetCostData.addDiscovererStamp(50, (long) 12);
+ this.votingStreetCostData.addDiscovererStamp(90, (long) 11);
+
+ VotingStreetCostData votingStreetCostDataAux = new VotingStreetCostData(50, 10);
+ votingStreetCostDataAux.addDiscovererStamp(15, (long) 1);
+ // cost discovered by the same car
+ votingStreetCostDataAux.addDiscovererStamp(15, (long) 2);
+ votingStreetCostDataAux.addDiscovererStamp(15, (long) 23);
+ votingStreetCostDataAux.addDiscovererStamp(51, (long) 24);
+ votingStreetCostDataAux.addDiscovererStamp(48, (long) 21);
+
+ this.votingStreetCostData.addNewStamps(votingStreetCostDataAux);
+
+ if (votingStreetCostData.getCostVotingMap().size() == 3) {
+ System.out.println("[PASS]: Number of costs is correct");
+ } else {
+ System.out.println("[FAIL]: WRONG: " + votingStreetCostData.getCostVotingMap().size() + " costs instead of 3");
+ }
+
+ votingStreetCostData.printVoteSession();
+ votingStreetCostData.isStreetCostUpdated();
+ votingStreetCostData.clearVoteSession();
+ System.out.println("Voted cost is: " + votingStreetCostData.getStreetCost());
+ votingStreetCostData.printVoteSession();
+ System.out.println();
+ }
+
+ public void testSomething() {
+ VotingStreetCostData votingStreetCostData = new VotingStreetCostData(50, 10);
+ myMap.put((long)1, votingStreetCostData);
+ VotingStreetCostData v = myMap.get((long)1);
+ v.addDiscovererStamp(20, (long)11);
+
+ myMap.get((long)1).printVoteSession();
+
+ }
+
+ public void testIsStreetCostUpdated() {
+ System.out.println("---- IS STREET COST UPDATED... ----");
+
+ VotingStreetCostData votingStreetCostData = new VotingStreetCostData(50, 10);
+ VotingStreetCostData votingStreetCostDataAux = new VotingStreetCostData(50, 10);
+
+ votingStreetCostDataAux.addDiscovererStamp(48, (long)11);
+ votingStreetCostDataAux.addDiscovererStamp(48, (long)12);
+ votingStreetCostDataAux.addDiscovererStamp(49, (long)13);
+
+ if (votingStreetCostData.isStreetCostUpdated()) {
+ System.out.println("[FAIL]");
+ } else {
+ System.out.println("[PASS]");
+ }
+
+ votingStreetCostData.addNewStamps(votingStreetCostDataAux);
+
+ if (votingStreetCostData.isStreetCostUpdated()) {
+ System.out.println("[PASS]");
+ } else {
+ System.out.println("[FAIL]");
+ }
+
+ if (votingStreetCostData.DYNAMIC_VOTING_THRESHOLD == 2) {
+ System.out.println("PASS");
+ } else {
+ System.out.println("FAIL");
+ }
+ votingStreetCostData.clearVoteSession();
+ if (votingStreetCostData.isStreetCostUpdated()) {
+ System.out.println("[FAIL]");
+ } else {
+ System.out.println("[PASS]");
+ }
+
+ votingStreetCostDataAux.getCostVotingMap().clear();
+ votingStreetCostDataAux.addDiscovererStamp(90, (long)111);
+ votingStreetCostData.addNewStamps(votingStreetCostDataAux);
+
+ if (votingStreetCostData.isStreetCostUpdated()) {
+ System.out.println("[FAIL]");
+ } else {
+ System.out.println("[PASS]");
+ }
+ System.out.println("Voted cost is: " + votingStreetCostData.getStreetCost());
+
+ votingStreetCostDataAux.addDiscovererStamp(92, (long)113);
+
+ votingStreetCostData.addNewStamps(votingStreetCostDataAux);
+ votingStreetCostData.printVoteSession();
+ if (votingStreetCostData.isStreetCostUpdated()) {
+ System.out.println("[PASS]");
+ } else {
+ System.out.println("[FAIL]");
+ }
+ System.out.println("Voted cost is: " + votingStreetCostData.getStreetCost());
+
+ if (votingStreetCostData.DYNAMIC_VOTING_THRESHOLD == 3) {
+ System.out.println("PASS");
+ } else {
+ System.out.println("FAIL");
+ }
+ }
+
+ public static void main(String[] args) {
+ VotingStreetCostDataTest votingStreetCostDataTest = new VotingStreetCostDataTest();
+ votingStreetCostDataTest.idTheSameCostTest();
+ votingStreetCostDataTest.addNewCostsTest();
+ votingStreetCostDataTest.hasNewStampsTest();
+ votingStreetCostDataTest.addNewStampsTest();
+ votingStreetCostDataTest.testIsStreetCostUpdated();
+ }
+}
diff --git a/src/application/streetCostSharing/StreetsCostSharing.java b/src/application/streetCostSharing/StreetsCostSharing.java
new file mode 100644
index 0000000..faab9c2
--- /dev/null
+++ b/src/application/streetCostSharing/StreetsCostSharing.java
@@ -0,0 +1,268 @@
+package application.streetCostSharing;
+import application.dynamic_routing.VotingStreetCostData;
+import model.GeoCar;
+import model.OSMgraph.Way;
+import model.OSMgraph.Node;
+import model.mobility.MobilityEngine;
+import model.parameters.Globals;
+import utils.tracestool.Utils;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * This class has a batch of functions useful for dynamic routes calculations.
+ * Also, it has a reference to car's hashmap of streets cost (each car
+ * has its own perspective about the streets costs)
+ *
+ */
+
+public class StreetsCostSharing {
+
+// pairs between each way id and its current cost
+// each car has its representation
+ protected HashMap streetGraphCost;
+
+ protected HashMap streetUpdates;
+ public static int MAX_DYNAMIC_VOTING_THRESHOLD = 7;
+ public static int MIN_DYNAMIC_VOTING_THRESHOLD = 2;
+
+ // percentage threshold for considering a street cost updated
+ public static double STREET_COST_UPDATE_THRESHOLD = 0.4;
+
+ /* street update constraints: CAR_MIN_TRACE_ON_STREET - percent
+ * CAR_MIN_TIME_ON_STREET - timestamps*/
+ public static double CAR_MIN_TRACE_ON_STREET = 0.5;
+ public static double CAR_MIN_TIME_ON_STREET = 10;
+
+ MobilityEngine mobilityEngine;
+ GeoCar car;
+
+
+ public HashMap getStreetUpdates() {
+ return streetUpdates;
+ }
+
+ public StreetsCostSharing(GeoCar car){
+ this.mobilityEngine = MobilityEngine.getInstance();
+ streetGraphCost = new HashMap();
+ this.streetUpdates = new HashMap();
+ this.car = car;
+ }
+
+ /* addNewUpdate and removeOutdatedInfo are functions for
+ * handling the updates regarding streets' costs by adding new costs
+ * or removing a info that already has been scattered*/
+
+ public void addNewUpdate(Long wayId, Double cost, Long discovererStamp) {
+
+ if (!this.streetUpdates.containsKey(wayId)) {
+ this.streetUpdates.put(wayId, new VotingStreetCostData(cost, this.car.getId()));
+ return;
+ }
+
+ VotingStreetCostData votingStreetCostData = this.streetUpdates.get(wayId);
+ votingStreetCostData.addDiscovererStamp(cost, discovererStamp);
+ }
+
+ /*The network is aware of this street costs. So they can be deleted from the
+ * sharing package (streetUpdates).
+ * There is no point in trying to achieve a number of votes for this cost. Given the fact
+ * that the network is aware of it, the car will receive a valid package in the near future*/
+ public void removeOutdatedInfo(HashMap outdatedCosts) {
+ Iterator itr = outdatedCosts.keySet().iterator();
+ long currentWayId;
+ double currentUpdateCost;
+ double outdatedCost;
+
+ while (itr.hasNext()) {
+ currentWayId = itr.next();
+
+ currentUpdateCost = streetUpdates.get(currentWayId).getStreetCost();
+ outdatedCost = outdatedCosts.get(currentWayId).getStreetCost();
+
+ /*check if the currentUpdateCost has been modified before receiving this update
+ * regarding outdated cost. So, if the difference between this 2 costs smaller than the THRESHOLD,
+ * there has been an update over this cost so it is not outdated anymore*/
+ if (Math.abs(currentUpdateCost - outdatedCost) <
+ (currentUpdateCost * StreetsCostSharing.STREET_COST_UPDATE_THRESHOLD)) {
+ streetUpdates.remove(currentWayId);
+ }
+ }
+ }
+
+/* returns the cost of street if it knows, otherwise calculate*/
+ public double getWayCost(Way way) {
+ double streetCost;
+
+ /*if the car does not know the street cost, take the first and the last node
+ * of the street. Calculate the distance between them using normal cruising speed.*/
+ if (!streetGraphCost.containsKey(way.id)) {
+ Node firstNode = way.nodes.firstElement();
+ Node lastNode = way.nodes.lastElement();
+
+ double streetLength = Utils.getRealDistanceAB(way, firstNode.id, lastNode.id);
+ streetCost = streetLength / (way.getMaximumSpeed() - 3);
+ streetGraphCost.put(way.id, streetCost);
+
+ } else {
+ streetCost = streetGraphCost.get(way.id);
+ }
+ return streetCost;
+ }
+
+
+ /* this function calculates the average street cost considering a car that traveled just a segment
+ * of the entire street. We cannot assume that the distance traveled by the car is the
+ * actual street length*/
+ public double calculateAverageCost (Way way, Node startingNode, Node finishNode, long wayStartTime, long wayFinishTime) {
+ Node firstNode = way.nodes.firstElement();
+ Node lastNode = way.nodes.lastElement();
+ long carTraceTime = wayFinishTime - wayStartTime;
+
+ double streetLength = Utils.getRealDistanceAB(way, firstNode.id, lastNode.id);
+
+// car's trace on the current street
+ double carTraceLength = Utils.getRealDistanceAB(way, startingNode.id, finishNode.id);
+
+// start node is not correct
+ if (carTraceLength > streetLength) {
+ return -1;
+ }
+
+ if (carTraceLength < (StreetsCostSharing.CAR_MIN_TRACE_ON_STREET * streetLength) ||
+ carTraceTime < StreetsCostSharing.CAR_MIN_TIME_ON_STREET) {
+ return -1;
+ }
+ double speed = carTraceLength / carTraceTime;
+
+ if (speed == 0) {
+ return -1;
+ }
+
+ return streetLength / speed;
+ }
+
+
+ /* this method is called when a car changes the street. So it is able to generate a report */
+ public void discoverNewWayCost(Way way, Node startingNode, Node finishNode, long startTime, long finishTime) {
+
+ double reportedCost = calculateAverageCost(way, startingNode, finishNode, startTime, finishTime);
+
+ if (reportedCost == -1) {
+ return;
+ }
+
+ double currentStreetCost = getWayCost(way);
+
+// use the percentage threshold to validate the new update
+ if (Math.abs(currentStreetCost - reportedCost) >
+ (currentStreetCost * StreetsCostSharing.STREET_COST_UPDATE_THRESHOLD)) {
+ streetGraphCost.put(way.id, reportedCost);
+
+ // add this new cost to the "StreetUpdates" in order to inform the next trafficLight
+ this.addNewUpdate(way.id, reportedCost, this.car.getId());
+ }
+
+ }
+
+ public static long counterMaliciousCosts = 0;
+ public static long counterRealCosts = 0;
+
+ /*function called when a the CarDynamicRoutingApp finds some validUpdates for this car*/
+ public void updateWayCost(long wayId, VotingStreetCostData receivedVotingData) {
+
+ /*stamps management*/
+ if (Globals.useVotingSystem) {
+ if (!this.streetUpdates.containsKey(wayId)) {
+ this.streetUpdates.put(wayId, receivedVotingData);
+ } else {
+
+ VotingStreetCostData currentVotingData = this.streetUpdates.get(wayId);
+ currentVotingData.addNewStamps(receivedVotingData);
+ }
+
+ VotingStreetCostData votingStreetCostData = this.streetUpdates.get(wayId);
+ if (votingStreetCostData.isStreetCostUpdated()) {
+
+ if (car.getCurrentRoute().getWayIdsSet().contains(wayId)) {
+
+ if (VotingStreetCostData.isTheSameStreetCost(receivedVotingData.getStreetCost(), StreetsCostSharingMalicious.DECREASED_VALUE)
+ || (VotingStreetCostData.isTheSameStreetCost(receivedVotingData.getStreetCost(), StreetsCostSharingMalicious.INCREASED_VALUE))) {
+ synchronized (StreetsCostSharing.class) {
+ counterMaliciousCosts++;
+ }
+ } else {
+ synchronized (StreetsCostSharing.class) {
+ counterRealCosts++;
+ }
+ }
+
+ if (receivedVotingData.getStreetCost() > this.getWayCost(mobilityEngine.getWay(wayId))) {
+ double oldCost = this.getWayCost(mobilityEngine.getWay(wayId));
+ this.streetGraphCost.put(wayId, receivedVotingData.getStreetCost());
+ this.streetUpdates.put(wayId, receivedVotingData);
+
+ car.appointForRouteRecalculation( receivedVotingData.getStreetCost()
+ - oldCost);
+ }
+ }
+
+ // clear the voting session and redistribute the new info
+ votingStreetCostData.clearVoteSession();
+ this.streetUpdates.put(wayId, votingStreetCostData);
+
+ /* the cost was voted, so we can add the cost to the KB*/
+ this.streetGraphCost.put(wayId, votingStreetCostData.getStreetCost());
+
+ } else {
+ /*otherwise, we have to wait until the cost is voted.
+ * the new votes are added to the KB in the first if statement*/
+ }
+ } else {
+ // do not use voting system
+
+ if (car.getCurrentRoute().getWayIdsSet().contains(wayId)) {
+
+ if (VotingStreetCostData.isTheSameStreetCost(receivedVotingData.getStreetCost(), StreetsCostSharingMalicious.DECREASED_VALUE)
+ || (VotingStreetCostData.isTheSameStreetCost(receivedVotingData.getStreetCost(), StreetsCostSharingMalicious.INCREASED_VALUE))) {
+ synchronized (StreetsCostSharing.class) {
+ counterMaliciousCosts++;
+ }
+ } else {
+ synchronized (StreetsCostSharing.class) {
+ counterRealCosts++;
+ }
+ }
+
+ if (receivedVotingData.getStreetCost() > this.getWayCost(mobilityEngine.getWay(wayId))) {
+ double oldCost = this.getWayCost(mobilityEngine.getWay(wayId));
+ this.streetGraphCost.put(wayId, receivedVotingData.getStreetCost());
+ this.streetUpdates.put(wayId, receivedVotingData);
+
+ car.appointForRouteRecalculation( receivedVotingData.getStreetCost()
+ - oldCost);
+ }
+ } else {
+ this.streetGraphCost.put(wayId, receivedVotingData.getStreetCost());
+ this.streetUpdates.put(wayId, receivedVotingData);
+ }
+ }
+
+ }
+
+ public double calculateEntireRouteCost(HashSet wayIdsSet) {
+ Iterator it = wayIdsSet.iterator();
+ long currentWayId = 0;
+ double total = 0;
+ double currentCost = 0;
+
+ while (it.hasNext()) {
+ currentWayId = it.next();
+ currentCost = this.getWayCost(mobilityEngine.getWay(currentWayId));
+ total += currentCost;
+ }
+ return total;
+ }
+}
diff --git a/src/application/streetCostSharing/StreetsCostSharingMalicious.java b/src/application/streetCostSharing/StreetsCostSharingMalicious.java
new file mode 100644
index 0000000..d9d776c
--- /dev/null
+++ b/src/application/streetCostSharing/StreetsCostSharingMalicious.java
@@ -0,0 +1,77 @@
+package application.streetCostSharing;
+
+import model.GeoCar;
+import model.OSMgraph.Node;
+import model.OSMgraph.Way;
+import model.mobility.MobilityEngine;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+public class StreetsCostSharingMalicious extends StreetsCostSharing {
+
+ public static final double INCREASED_VALUE = 4000;
+ public static final double DECREASED_VALUE = 1;
+ public static final int REPORT_MULTIPLICATION_FACTOR = 10;
+ ArrayList wayIdArr;
+
+ public StreetsCostSharingMalicious(GeoCar car) {
+
+ super(car);
+ wayIdArr = new ArrayList(MobilityEngine.getInstance().streetsGraph.keySet());
+
+ }
+
+ @Override
+ public void discoverNewWayCost(Way way, Node startingNode, Node finishNode, long startTime, long finishTime) {
+ int counter = 0;
+ Random rand = new Random();
+ long wayId;
+
+ switch (super.car.getPersonalityType()) {
+ case MALICIOUS_RANDOM:
+
+ while (counter < REPORT_MULTIPLICATION_FACTOR) {
+ int position = rand.nextInt(wayIdArr.size());
+ wayId = wayIdArr.get(position);
+
+ if (rand.nextInt(10) % 2 == 0)
+ super.addNewUpdate(wayId, StreetsCostSharingMalicious.INCREASED_VALUE, super.car.getId());
+ else
+ super.addNewUpdate(wayId, StreetsCostSharingMalicious.DECREASED_VALUE, super.car.getId());
+ counter++;
+ }
+
+ if (rand.nextInt(10) % 2 == 0) {
+ super.addNewUpdate(way.id, StreetsCostSharingMalicious.INCREASED_VALUE, super.car.getId());
+ } else {
+ super.addNewUpdate(way.id, StreetsCostSharingMalicious.DECREASED_VALUE, super.car.getId());
+ }
+ break;
+
+ case MALICIOUS_INCREASED:
+
+ while (counter < REPORT_MULTIPLICATION_FACTOR) {
+ int position = rand.nextInt(wayIdArr.size());
+ wayId = wayIdArr.get(position);
+ super.addNewUpdate(wayId, StreetsCostSharingMalicious.INCREASED_VALUE, super.car.getId());
+ counter++;
+ }
+ super.addNewUpdate(way.id, StreetsCostSharingMalicious.INCREASED_VALUE, super.car.getId());
+ break;
+
+ case MALICIOUS_DECREASED:
+
+ while (counter < REPORT_MULTIPLICATION_FACTOR) {
+ int position = rand.nextInt(wayIdArr.size());
+ wayId = wayIdArr.get(position);
+ super.addNewUpdate(wayId, StreetsCostSharingMalicious.DECREASED_VALUE, super.car.getId());
+ counter++;
+ }
+ super.addNewUpdate(way.id, StreetsCostSharingMalicious.DECREASED_VALUE, super.car.getId());
+ break;
+ }
+
+ }
+
+}
diff --git a/src/controller/network/NetworkWiFi.java b/src/controller/network/NetworkWiFi.java
index fdb00e8..fbea082 100644
--- a/src/controller/network/NetworkWiFi.java
+++ b/src/controller/network/NetworkWiFi.java
@@ -1,8 +1,8 @@
package controller.network;
-import java.util.ArrayList;
-import java.util.List;
+import java.util.*;
+import application.dynamic_routing.TrafficLightDynamicRoutingApp;
import model.Entity;
import model.GeoCar;
import model.GeoServer;
@@ -98,7 +98,95 @@ public NetworkInterface discoverClosestServer() {
return serverInRange.getNetworkInterface(this.getType());
}
-
+
+ private boolean trafficLightDiffers(GeoTrafficLightMaster current, GeoTrafficLightMaster tl1, GeoTrafficLightMaster tl2, GeoTrafficLightMaster tl3) {
+ if (current != tl1 && current != tl2 && current != tl3)
+ return true;
+ return false;
+ }
+
+
+ /* this function discovers the closest neighbour from each geographic direction (N S E W)*/
+ public HashMap discoverClosestTrafficLightsGeographic() {
+ Entity owner = getOwner();
+ ArrayList mtl = (ArrayList) owner.getMasterTrafficLights();
+
+ GeoTrafficLightMaster mtlSouth = null;
+ GeoTrafficLightMaster mtlNorth = null;
+ GeoTrafficLightMaster mtlEast = null;
+ GeoTrafficLightMaster mtlWest = null;
+
+ double maxDistSouth = Double.MAX_VALUE;
+ double maxDistNorth = Double.MAX_VALUE;
+ double maxDistEast = Double.MAX_VALUE;
+ double maxDistWest = Double.MAX_VALUE;
+ double dist = 0;
+
+ for (int i = 0; i < mtl.size(); i++) {
+ GeoTrafficLightMaster currentTrafficLight = mtl.get(i);
+ dist = Utils.distance(owner.getCurrentPos().lat, owner.getCurrentPos().lon,
+ currentTrafficLight.getCurrentPos().lat, currentTrafficLight.getCurrentPos().lon);
+
+ // NORTH case
+ if (currentTrafficLight.getCurrentPos().lon < owner.getCurrentPos().lon) {
+ if (dist < maxDistNorth && dist < RoutingApplicationParameters.distMax
+ && currentTrafficLight.getId() != owner.getId()
+ && trafficLightDiffers(currentTrafficLight, mtlSouth, mtlEast, mtlWest)) {
+ maxDistNorth = dist;
+ mtlNorth = currentTrafficLight;
+ }
+ }
+
+ // SOUTH case
+ if (currentTrafficLight.getCurrentPos().lon > owner.getCurrentPos().lon) {
+ if (dist < maxDistSouth && dist < RoutingApplicationParameters.distMax
+ && currentTrafficLight.getId() != owner.getId()
+ && trafficLightDiffers(currentTrafficLight, mtlNorth, mtlEast, mtlWest)) {
+ maxDistSouth = dist;
+ mtlSouth = currentTrafficLight;
+ }
+ }
+
+ // EAST case
+ if (currentTrafficLight.getCurrentPos().lat > owner.getCurrentPos().lat) {
+ if (dist < maxDistEast && dist < RoutingApplicationParameters.distMax
+ && currentTrafficLight.getId() != owner.getId()
+ && trafficLightDiffers(currentTrafficLight, mtlSouth, mtlNorth, mtlWest)) {
+ maxDistEast = dist;
+ mtlEast = currentTrafficLight;
+ }
+ }
+
+ // WEST case
+ if (currentTrafficLight.getCurrentPos().lat < owner.getCurrentPos().lat) {
+ if (dist < maxDistWest && dist < RoutingApplicationParameters.distMax
+ && currentTrafficLight.getId() != owner.getId()
+ && trafficLightDiffers(currentTrafficLight, mtlSouth, mtlEast, mtlNorth)) {
+ maxDistWest = dist;
+ mtlWest = currentTrafficLight;
+ }
+ }
+
+ }
+
+ HashMap ret = new HashMap();
+ if (maxDistNorth < RoutingApplicationParameters.distMax && mtlNorth != null) {
+ ret.put(TrafficLightDynamicRoutingApp.NORTH_INDEX, mtlNorth.getNetworkInterface(this.getType()));
+ }
+ if (maxDistSouth < RoutingApplicationParameters.distMax && mtlSouth != null) {
+ ret.put(TrafficLightDynamicRoutingApp.SOUTH_INDEX, mtlSouth.getNetworkInterface(this.getType()));
+ }
+ if (maxDistEast < RoutingApplicationParameters.distMax && mtlEast != null) {
+ ret.put(TrafficLightDynamicRoutingApp.EAST_INDEX, mtlEast.getNetworkInterface(this.getType()));
+ }
+ if (maxDistWest < RoutingApplicationParameters.distMax && mtlWest != null) {
+ ret.put(TrafficLightDynamicRoutingApp.WEST_INDEX, mtlWest.getNetworkInterface(this.getType()));
+ }
+
+ return ret;
+ }
+
+
public List discoverClosestsTrafficLightMasters() {
Entity owner = getOwner();
ArrayList mtl = (ArrayList) owner.getMasterTrafficLights();
diff --git a/src/controller/newengine/EngineUtils.java b/src/controller/newengine/EngineUtils.java
index 4d0fff9..9022d96 100644
--- a/src/controller/newengine/EngineUtils.java
+++ b/src/controller/newengine/EngineUtils.java
@@ -1,5 +1,6 @@
package controller.newengine;
+import application.dynamic_routing.CarDynamicRoutingApp;
import gui.TrafficLightView;
import gui.Viewer;
@@ -15,16 +16,8 @@
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
import java.util.Map.Entry;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.TreeMap;
-import java.util.Vector;
import java.util.logging.Logger;
import application.Application;
@@ -44,6 +37,7 @@
import model.mobility.MobilityEngine;
import model.parameters.Globals;
import model.parameters.MapConfig;
+import model.personality.PersonalityTypes;
import utils.ComputeAverageSpeeds;
import utils.ComputeStreetVisits;
import utils.SphericalMercator;
@@ -58,6 +52,16 @@
* Class used to read the servers and the cars from files
*
*/
+
+class GeoCarComparator implements Comparator {
+
+ @Override
+ public int compare(GeoCar o1, GeoCar o2) {
+ return (int) (o2.getRouteIncreasedCostPercentage() - o1.getRouteIncreasedCostPercentage());
+ }
+}
+
+
public final class EngineUtils {
/** Logger used by this class */
@@ -69,6 +73,9 @@ public final class EngineUtils {
/* Server info */
static double latEdge = 0d, lonEdge = 0d;
static long rows = 0, cols = 0;
+
+// this queue is useful to choose which car is next for route dynamic update
+ public static PriorityQueue carsQueue = new PriorityQueue<>(new GeoCarComparator());
private EngineUtils() {}
@@ -89,6 +96,8 @@ static TreeMap getCars(String carListFilename, Viewer viewer, Mobil
BufferedReader br = new BufferedReader(new InputStreamReader(fstream));
String line;
+ int malicious_index = 0;
+
/* Read data about traces */
while ((line = br.readLine()) != null) {
/* If the simulation requires just a fraction of the cars
@@ -106,11 +115,22 @@ static TreeMap getCars(String carListFilename, Viewer viewer, Mobil
st.nextToken(); /* routes = Utils.readCarTraces(path);
-
+
GeoCar car = new GeoCar(count);
car.setRoutes(routes);
+// added
+// car.setSameEndPointToAllRoutes();
+
+ if (malicious_index < Globals.maliciousCars) {
+ malicious_index++;
+ car.setPersonalityType(Globals.maliciousPersonalityType);
+ }
+
+
if (count == 183)
System.out.println("read +");
@@ -120,11 +140,17 @@ static TreeMap getCars(String carListFilename, Viewer viewer, Mobil
NetworkInterface netInterface = NetworkUtils.activateNetworkInterface(type, car);
car.addNetworkInterface(netInterface);
}
-
+
+ /*add car's routing app*/
+ if (Globals.costSharingApps) {
+ Globals.activeApplications.add(ApplicationType.CAR_ROUTING_APP);
+ }
+
/* Create each application which is defined */
for( ApplicationType type : Globals.activeApplications )
{
Application app = ApplicationUtils.activateApplicationCar(type, car);
+
if( app == null )
{
logger.info(" Failed to create application with type " + type);
@@ -136,6 +162,7 @@ static TreeMap getCars(String carListFilename, Viewer viewer, Mobil
viewer.addCar(car);
cars.put(car.getId(), car);
count++;
+
}
br.close();
} catch (IOException ex) {
@@ -147,7 +174,7 @@ static TreeMap getCars(String carListFilename, Viewer viewer, Mobil
ex.printStackTrace();
}
}
- System.out.println("cars" + cars.size());
+ System.out.println("cars " + cars.size());
return cars;
}
@@ -351,6 +378,18 @@ private static void addTrafficLightApps(GeoTrafficLightMaster master) {
else
master.addApplication( app );
}
+
+ if (Globals.costSharingApps) {
+ type = ApplicationType.TRAFFIC_LIGHT_ROUTING_APP;
+ app = ApplicationUtils.activateApplicationTrafficLight(type, master);
+ if( app == null )
+ {
+ logger.info(" Failed to create application with type " + type);
+ }
+ else {
+ master.addApplication(app);
+ }
+ }
}
/***
@@ -363,7 +402,6 @@ private static TreeMap readExistingTrafficLights(
FileInputStream fstream = null;
TreeMap trafficLights = new TreeMap();
-
try {
fstream = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(fstream));
@@ -441,7 +479,7 @@ private static TreeMap readExistingTrafficLights(
ex.printStackTrace();
}
}
- System.out.println(trafficLights.size());
+ System.out.println("nr of tl: " + trafficLights.size());
return trafficLights;
}
@@ -455,7 +493,7 @@ private static TreeMap readExistingTrafficLights(
*/
private static TreeMap readTrafficLights(
String inFile, String outFile, Viewer viewer, MobilityEngine mobilityEngine) {
-
+
FileInputStream fstream = null;
TreeMap trafficLights = new TreeMap();
//if traffic lights were not loaded before, read them from the config
diff --git a/src/controller/newengine/SimulationEngine.java b/src/controller/newengine/SimulationEngine.java
index fe35e3b..c37c1df 100644
--- a/src/controller/newengine/SimulationEngine.java
+++ b/src/controller/newengine/SimulationEngine.java
@@ -1,5 +1,6 @@
package controller.newengine;
+import application.dynamic_routing.TrafficLightDynamicRoutingApp;
import gui.View;
import gui.Viewer;
@@ -24,16 +25,13 @@
import model.parameters.MapConfig;
import model.parameters.MapConfiguration;
import model.threadpool.ThreadPool;
-import model.threadpool.tasks.CarApplicationsRun;
-import model.threadpool.tasks.CarMove;
-import model.threadpool.tasks.CarPrepareMove;
-import model.threadpool.tasks.ServerApplicationsRun;
-import model.threadpool.tasks.TrafficLightApplicationsRun;
-import model.threadpool.tasks.TrafficLightChangeColor;
+import model.threadpool.tasks.*;
import controller.engine.EngineInterface;
import controller.network.NetworkType;
+import application.streetCostSharing.StreetsCostSharing;
- /**
+
+/**
* Class used to represent the brain of the simulator.
* It reads the data for cars and servers applies the designated applications to be run on them.
* Runs the simulation steps for each time frame (see the Runnable hidden object)
@@ -57,7 +55,7 @@ public class SimulationEngine implements EngineInterface {
public TreeMap entities;
/** Graphic interface visualizer */
- private Viewer viewer;
+ public Viewer viewer;
/** Thread Pool reference */
private ThreadPool threadPool;
@@ -68,9 +66,13 @@ public class SimulationEngine implements EngineInterface {
/** Simulation time */
private long time = 0;
+ private int MAX_CARS_UPDATE_ROUTE = 10;
+
private static final SimulationEngine _instance = new SimulationEngine();
private static Object lock = null;
+ public static boolean oneMessage = true;
+
/**
* Constructor is called just once, so all initializations are safe to be done here...
*/
@@ -100,15 +102,28 @@ public static SimulationEngine getInstance() {
}
public void setUp() {
-
+
entities.putAll(EngineUtils.getCars(getMapConfig().getTracesListFilename(), viewer, mobilityEngine) );
entities.putAll(EngineUtils.getServers(getMapConfig().getAccessPointsFilename(), viewer, mobilityEngine) );
if (Globals.useTrafficLights || Globals.useDynamicTrafficLights) {
entities.putAll(EngineUtils.getTrafficLights(getMapConfig().getTrafficLightsFilename(),
getMapConfig().getTrafficLightsLoaded(), viewer, mobilityEngine));
}
+
+ /* the applications are added to the traffic lights in EngineUtils.
+ * Just need to call "postConstructInit" after all of the traffic lights are
+ * generated*/
+ if (Globals.dynamicRoutes && Globals.costSharingApps) {
+ for (Entity e : entities.values()) {
+ if (e instanceof GeoTrafficLightMaster) {
+ TrafficLightDynamicRoutingApp app = (TrafficLightDynamicRoutingApp)e.
+ getApplication(ApplicationType.TRAFFIC_LIGHT_ROUTING_APP);
+ app.postConstructInit();
+ }
+ }
+ }
- if (Globals.activeApplications.contains(ApplicationType.ROUTING_APP)) {
+ if (Globals.activeApplications.contains(ApplicationType.ROUTING_APP) && !Globals.dynamicRoutes) {
for (Entity e : entities.values()) {
if (e instanceof GeoServer) {
EngineUtils.addApplicationToServer((GeoServer) e);
@@ -157,6 +172,21 @@ public void run() {
}
+// select the cars for route update. All cars are stored in a queue for equal chances
+ if (time % 3 == 0 && Globals.dynamicRoutes) {
+ if (EngineUtils.carsQueue.size() > 0) {
+ GeoCar currentCar = EngineUtils.carsQueue.poll();
+ currentCar.setTurnToUpdateRoute(true);
+ currentCar.setAppointedForRouteRecalculation(false);
+ currentCar.setRouteIncreasedCost(0);
+ }
+ }
+
+ if (time % 100 == 0 && Globals.maliciousCars > 0) {
+ System.out.println("TIME: " + time + " Malicious costs: " + StreetsCostSharing.counterMaliciousCosts + " Real: " + StreetsCostSharing.counterRealCosts);
+ }
+
+
threadPool.waitForThreadPoolProcessing();
for (Entity e : entities.values()) {
@@ -188,8 +218,9 @@ public void run() {
viewer.updateCarPositions();
viewer.updateTrafficLightsColors();
viewer.setTime("" + time);
+
- if( (time + 2)% RoutingApplicationParameters.SamplingInterval == 0)
+ if( (time + 5)% RoutingApplicationParameters.SamplingInterval == 0)
{
System.out.println("WRITTING ROUTES TIME TO FILES!");
for (Entity e : entities.values()) {
diff --git a/src/downloader/DownloadCore.java b/src/downloader/DownloadCore.java
index 7d32b0c..d091f00 100644
--- a/src/downloader/DownloadCore.java
+++ b/src/downloader/DownloadCore.java
@@ -174,8 +174,8 @@ public void execute(String city) {
String traces = curDir.getAbsolutePath();
parseConfigFile();
-
- if (city.equalsIgnoreCase("rome")) {
+
+ if (city.equalsIgnoreCase("src/configurations/simulator/rome")) {
String archive = t.getAbsolutePath() + File.separator + "rome.zip";
if (mode.equalsIgnoreCase("D")) {
@@ -186,7 +186,8 @@ public void execute(String city) {
unZipIt(archive, traces);
- } else if (city.equalsIgnoreCase("beijing")) {
+ } else if (city.equalsIgnoreCase("src/configurations/simulator/beijing")) {
+
String archive = t.getAbsolutePath() + File.separator + "beijing.zip";
if (mode.equalsIgnoreCase("D")) {
@@ -197,7 +198,7 @@ public void execute(String city) {
unZipIt(archive, traces);
- } else if (city.equalsIgnoreCase("sanfrancisco")) {
+ } else if (city.equalsIgnoreCase("src/configurations/simulator/sanfrancisco")) {
String archive = t.getAbsolutePath() + File.separator + "sanfrancisco.zip";
if (mode.equalsIgnoreCase("D")) {
diff --git a/src/downloader/Downloader.java b/src/downloader/Downloader.java
index 5102813..2c87c58 100644
--- a/src/downloader/Downloader.java
+++ b/src/downloader/Downloader.java
@@ -38,7 +38,7 @@ private void extractCity(String propFile) {
city = propF[0];
}
-
+
public void downloadTraces(String propFile) {
// Extract the city name
diff --git a/src/gui/TrafficLightView.java b/src/gui/TrafficLightView.java
index f0ffa98..27be899 100644
--- a/src/gui/TrafficLightView.java
+++ b/src/gui/TrafficLightView.java
@@ -77,6 +77,11 @@ public void run() {
});
}
+
+ public void messageAlert() {
+ Color c = this.getColor();
+ setColor(Color.blue);
+ }
public void changeColor() {
if (trafficLightColor == Color.red) {
diff --git a/src/model/DynamicRoutes.java b/src/model/DynamicRoutes.java
new file mode 100644
index 0000000..f28c969
--- /dev/null
+++ b/src/model/DynamicRoutes.java
@@ -0,0 +1,167 @@
+package model;
+
+import model.OSMgraph.Node;
+import model.OSMgraph.Way;
+import model.mobility.MobilityEngine;
+import application.streetCostSharing.StreetsCostSharing;
+import utils.Pair;
+import utils.tracestool.Utils;
+
+import java.util.*;
+
+public class DynamicRoutes {
+ public TreeMap streetsGraph;
+ MobilityEngine mobilityEngine;
+ StreetsCostSharing streetsCostSharing;
+
+ DynamicRoutes(StreetsCostSharing dr_utils) {
+ mobilityEngine = MobilityEngine.getInstance();
+ streetsGraph = mobilityEngine.streetsGraph;
+ streetsCostSharing = dr_utils;
+ }
+
+
+ public Pair, HashSet> findPath(Node startNode, Node stopNode) {
+ List intersectionsList = new ArrayList();
+ TreeMap,Node> path = new TreeMap,Node>();
+ TreeMap,Double> distance = new TreeMap,Double>();
+ HashSet wayIdsSet = new HashSet();
+
+ /*checks for startNode and stopNode correctness. +
+ * if case, try to correct them by considering the closest nodes*/
+
+ int ok = 1;
+ if( startNode.id != -1 ) {
+ Way sW = streetsGraph.get(startNode.wayId);
+ if(sW != null && sW.neighs.get(startNode.id) != null ) {
+ startNode = sW.getNode(startNode.id);
+ } else {
+ ok = 0;
+ }
+
+ } else {
+ ok = 0;
+ }
+ if (ok == 0) {
+ startNode = Utils.getClosestJointFromCrtPosition(
+ streetsGraph, startNode.wayId,
+ streetsGraph.get(startNode.wayId).getClosestNode( startNode.lat, startNode.lon)
+ );
+ }
+
+ ok = 1;
+ if( stopNode.id != -1 ) {
+ Way sW = streetsGraph.get(stopNode.wayId);
+ if(sW != null && sW.neighs.get(stopNode.id) != null ) {
+ stopNode = sW.getNode(stopNode.id);
+ } else {
+ ok = 0;
+ }
+
+ } else {
+ ok = 0;
+ }
+ if (ok == 0) {
+ stopNode = Utils.getClosestJointFromCrtPosition(
+ streetsGraph, stopNode.wayId,
+ streetsGraph.get(stopNode.wayId).getClosestNode( stopNode.lat, stopNode.lon)
+ );
+ }
+
+ if( startNode == null || stopNode == null ) {
+ return null;
+ }
+
+ /* start the path finding algorithm*/
+
+ Pair currentPair = new Pair(startNode.id, startNode.wayId);
+ LinkedList> q = new LinkedList>();
+ Node currentNode, jointNode;
+ Way currentWay;
+ Vector> neighs = new Vector>();
+ HashMap parents = new HashMap();
+ double currentDistance, parentDistance;
+
+ q.addLast(currentPair);
+ distance.put(currentPair, (double) 0);
+
+ /*explore the graph in a bfs manner*/
+ ok = 1;
+ while(!q.isEmpty()) {
+ if (currentPair.getFirst() == stopNode.id && currentPair.getSecond() == stopNode.wayId) {
+ break;
+ }
+ currentWay = streetsGraph.get(currentPair.getSecond());
+ currentNode = currentWay.getNode(currentPair.getFirst());
+
+// make sure that startNode has the same way id with what I am looking for at the backpropagation
+// through parents list
+ if (ok == 1) {
+ startNode = currentNode;
+ ok = 0;
+ }
+
+// intersection's neighbours
+ neighs = Utils.getDirectLinkedJointsFromCrtPosition(streetsGraph, currentNode);
+
+ for( Pair entry : neighs ) {
+ jointNode = streetsGraph.get(entry.getSecond()).getNode(entry.getFirst());
+ currentWay = streetsGraph.get(currentNode.wayId);
+ if( !q.contains(entry) )
+ {
+
+ /*calculate the distance between current and next node.
+ * add the distance from the source.
+ * the cost is measured in seconds*/
+ boolean isDistanceUpdated = false;
+ parentDistance = distance.get(currentPair);
+ currentDistance = parentDistance + streetsCostSharing.getWayCost(currentWay);
+
+ /*the nodes are explored in 2 scenarios:
+ * - the node has not been visited before
+ * - it has been visited, but there is a time improvement in reaching that node
+ * otherwise, the node will not be explored */
+
+ if (distance.containsKey(entry)) {
+
+ if (currentDistance < distance.get(entry)) {
+ distance.put(entry, currentDistance);
+ isDistanceUpdated = true;
+ }
+
+ } else {
+ distance.put(entry, currentDistance);
+ isDistanceUpdated = true;
+ }
+
+ if (isDistanceUpdated) {
+ q.addLast(new Pair(jointNode.id, jointNode.wayId));
+ parents.put(jointNode, currentNode);
+ }
+ }
+ }
+ currentPair = q.poll();
+ }
+
+ // build the new path
+ try {
+
+ currentNode = streetsGraph.get(currentPair.getSecond()).getNode(currentPair.getFirst());
+
+ while (currentNode.id != -1 && currentNode.id != startNode.id) {
+
+ intersectionsList.add(0, parents.get(currentNode));
+ currentNode = parents.get(currentNode);
+ wayIdsSet.add(currentNode.wayId);
+ }
+
+ } catch (Exception e) {
+ System.out.println(e.getMessage());
+ }
+
+ return new Pair<>(intersectionsList, wayIdsSet);
+
+ }
+
+
+}
diff --git a/src/model/GeoCar.java b/src/model/GeoCar.java
index 3d4b237..b47d916 100644
--- a/src/model/GeoCar.java
+++ b/src/model/GeoCar.java
@@ -4,26 +4,28 @@
import java.awt.Color;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.*;
+import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
+import application.streetCostSharing.StreetsCostSharing;
+import application.streetCostSharing.StreetsCostSharingMalicious;
import controller.network.NetworkInterface;
import controller.network.NetworkType;
import controller.network.NetworkWiFi;
+import controller.newengine.EngineUtils;
import controller.newengine.SimulationEngine;
import model.OSMgraph.Node;
+import model.OSMgraph.Way;
import model.mobility.MobilityEngine;
import model.network.Message;
import model.network.MessageType;
import model.parameters.Globals;
import model.personality.Personality;
+import model.personality.PersonalityTypes;
import model.personality.RegularPersonality;
-import utils.ComputeAverageFuelConsumption;
-import utils.Pair;
-import utils.TraceParsingTool;
+import utils.*;
import utils.tracestool.Utils;
import application.Application;
import application.ApplicationType;
@@ -70,8 +72,13 @@ public class GeoCar extends Entity {
/** Used to count the number of times the car has stayed still */
private int still = 0;
+ private boolean ableToUpdate = true;
+
private boolean stoppedAtTrafficLight = false;
+
+ private boolean appointedForRouteRecalculation = false;
+
/**
* List of nodes between current position and next position. The list is sorted
* from the current position towards next position.
@@ -83,10 +90,54 @@ public class GeoCar extends Entity {
/** The car in front of this car. Is updated at every iteration */
private Pair elementAhead = null;
+ public static int ROUTE_CHECK_TIMESTAMP = 10;
+ private boolean turnToUpdateRoute = false;
+
/** Route length in time information */
StringBuffer routesTime = new StringBuffer();
// StringBuffer tracesTime = new StringBuffer();
+ /** reference to algorithms for dynamic routes, useful for routes updating */
+ StreetsCostSharing streetsCostSharing;
+
+ public StreetsCostSharing getStreetsCostSharing() {
+ return streetsCostSharing;
+ }
+
+ public void setStreetsCostSharing(StreetsCostSharing streetsCostSharing) {
+ this.streetsCostSharing = streetsCostSharing;
+ }
+
+ DynamicRoutes dynamicRoutes;
+
+ private PersonalityTypes personalityType = PersonalityTypes.REGULAR;
+
+
+ public PersonalityTypes getPersonalityType() {
+ return personalityType;
+ }
+ public boolean isAppointedForRouteRecalculation() {
+ return appointedForRouteRecalculation;
+ }
+
+ public void setAppointedForRouteRecalculation(boolean appointedForRouteRecalculation) {
+ this.appointedForRouteRecalculation = appointedForRouteRecalculation;
+ }
+
+ public void setPersonalityType(PersonalityTypes personalityType) {
+ this.personalityType = personalityType;
+ this.streetsCostSharing = new StreetsCostSharingMalicious(this);
+ }
+
+ /** Street costs related elements (to update the costs dynamically)*/
+ private Way currentWay;
+ private Node startingNode;
+ private long wayStartTime;
+ private HashMap costSharedCars;
+ private HashSet costSharedTrafficLight;
+ private double entireRouteCost = 0;
+ public static double ROUTE_UPDATE_THRESHOLD = 0.1;
+
public GeoCar(int id) {
this(id, new RegularPersonality());
}
@@ -98,6 +149,10 @@ public GeoCar(int id, Personality driver) {
this.setCurrentPos(null);
this.setNextPos(null);
this.beginNewRoute = true;
+ costSharedCars = new HashMap();
+ costSharedTrafficLight = new HashSet();
+ streetsCostSharing = new StreetsCostSharing(this);
+ dynamicRoutes = new DynamicRoutes(streetsCostSharing);
}
public void setSpeed(double speed) {
@@ -286,6 +341,10 @@ public void updateSpeedTrafficLightAhead() {
long wayId = this.getCurrentPos().wayId;
int direction = -this.getCurrentPos().direction;
+ /*if there is a traffic light ahead, share the costs updates with it*/
+ if (Globals.costSharingApps && distance < 25)
+ sendUpdatesToTrafficLight(trafficLightInFront);
+
/* The car is close to the traffic light and the traffic light is red -> stop */
if (distance < 25 && trafficLightInFront.getTrafficLightColor(wayId, direction) == Color.red
&& !isStoppedAtTrafficLight()) {
@@ -295,6 +354,7 @@ public void updateSpeedTrafficLightAhead() {
setStopppedAtTrafficLight(true);
if (Globals.useTrafficLights || Globals.useDynamicTrafficLights)
sendDataToTrafficLight(trafficLightInFront, wayId, direction, this.getCurrentPos());
+
} else {
/*
@@ -314,7 +374,7 @@ && isStoppedAtTrafficLight()) {
}
/***
- * Sends a message via WiFi to the master trafic light in front to notify it
+ * Sends a message via WiFi to the master traffic light in front to notify it
* about it's presence. The master traffic light will put the car to the
* corresponding waiting queue.
*
@@ -382,11 +442,167 @@ public Double getCongestionDegree(Double AVGspeed, Double maxSpeed) {
return g;
}
+ private int ok = 1;
+
+/** task that calls findPath method from dynamicRoutes.
+ this task is sent to executor service */
+ Callable, HashSet>> updateRouteTask = new Callable, HashSet>>() {
+
+ @Override
+ public Pair, HashSet> call() throws Exception {
+
+ return dynamicRoutes.findPath(getCurrentRoute().getIntersectionList().get(0),
+ getCurrentRoute().getIntersectionList().get(getCurrentRoute().getIntersectionList().size() - 1));
+ }
+ };
+
+ private ExecutorService executor = Executors.newCachedThreadPool();
+
+ private boolean updatedRoute = false;
+
+ /** function called when it's current car's turn to find a new path.
+ * add the task to the thread pool.
+ * update the intersectionList and wayIdsSet*/
+ public void updateGeoCarRoute() {
+
+ Future, HashSet>> future = executor.submit(updateRouteTask);
+
+ try {
+ Pair, HashSet> updateResult = future.get(200, TimeUnit.MILLISECONDS);
+
+ this.getCurrentRoute().setIntersectionList(updateResult.getFirst());
+ this.getCurrentRoute().setWayIdsSet(updateResult.getSecond());
+ this.updatedRoute = true;
+
+ } catch (Exception e) {
+ // handle other exceptions
+ } finally {
+ future.cancel(true);
+ }
+
+ }
+
+
+ public void setRouteIncreasedCost(double routeIncreasedCost) {
+ this.routeIncreasedCost = routeIncreasedCost;
+ }
+
+ private double routeIncreasedCost = 0;
+
+ public double getRouteIncreasedCostPercentage() {
+ return routeIncreasedCostPercentage;
+ }
+
+ private double routeIncreasedCostPercentage = 0;
+
+
+ /** this method has the role to appoint the car for route recalculation
+ * it is called from streetsCostSharing.updateWayCost when a street within
+ * this car's route is updated (has a different cost)*/
+
+ public void appointForRouteRecalculation(double increasedWayCost) {
+ routeIncreasedCost += increasedWayCost;
+ routeIncreasedCostPercentage = (routeIncreasedCost * 100) / entireRouteCost;
+
+ if (!this.appointedForRouteRecalculation
+ && routeIncreasedCost > this.entireRouteCost * GeoCar.ROUTE_UPDATE_THRESHOLD) {
+
+ EngineUtils.carsQueue.add(this);
+ this.appointedForRouteRecalculation = true;
+ }
+ }
+
+ public void setTurnToUpdateRoute(boolean t) {
+ this.turnToUpdateRoute = t;
+ }
+
+
+ /** method for street change detection.
+ * When a car change the current street, it will calculate the new discovered cost for the old current street
+ * and it will start a new timer for the new current street*/
+
+ public void updateCostRelatedElements() {
+
+ if (currentWay.id != getCurrentRoute().getIntersectionList().get(0).wayId) {
+ // get the finish node of the current way
+ Node finishNode = this.currentWay.getClosestNode(this.getCurrentPos().lat, this.getCurrentPos().lon);
+ streetsCostSharing.discoverNewWayCost(currentWay, startingNode, finishNode, this.wayStartTime, SimulationEngine.getInstance().getSimulationTime());
+
+ /*delete the way that has been traveled from the set (otherwise, risk to appoint for route recalc based
+ on your own report for a street that you passed) */
+ getCurrentRoute().getWayIdsSet().remove(currentWay.id);
+
+ this.currentWay = mobility.streetsGraph.get(getCurrentRoute().getIntersectionList().get(0).wayId);
+ this.wayStartTime = SimulationEngine.getInstance().getSimulationTime();
+
+ this.startingNode = this.currentWay.getClosestNode(this.getCurrentPos().lat, this.getCurrentPos().lon);
+ }
+ }
+
+
+ public void sendUpdatesToTrafficLight(GeoTrafficLightMaster trafficLightMaster) {
+
+ if (this.getStreetsCostSharing().getStreetUpdates().isEmpty() ||
+ costSharedTrafficLight.contains(trafficLightMaster.getId())) {
+ return;
+ }
+
+ NetworkInterface networkInterface = this.getNetworkInterface(NetworkType.Net_WiFi);
+
+ NetworkInterface discoveredTrafficLightMaster = ((NetworkWiFi) networkInterface).discoverTrafficLight(trafficLightMaster);
+ Message message = new Message(this.getId(), discoveredTrafficLightMaster.getOwner().getId(), this.streetsCostSharing.getStreetUpdates().clone(),
+ MessageType.CAR_INFORMS, ApplicationType.TRAFFIC_LIGHT_ROUTING_APP);
+ networkInterface.putMessage(message);
+
+ costSharedTrafficLight.add(trafficLightMaster.getId());
+ }
+
+
+ public void sendUpdatesToCar(GeoCar neighbourCar) {
+ NetworkInterface networkInterface = this.getNetworkInterface(NetworkType.Net_WiFi);
+
+ Message message = new Message(this.getId(), neighbourCar.getId(), this.streetsCostSharing.getStreetUpdates().clone(),
+ MessageType.CAR_INFORMS, ApplicationType.CAR_ROUTING_APP);
+ networkInterface.putMessage(message);
+ }
+
+ public void checkForNeighboursForCostSharing() {
+ /*Get neighbours for cars' cost sharing
+ * check if there was already an exchange of messages with that car*/
+ GeoCar carOnOppositeSide = mobility.getCarOnOppositeSide(this);
+
+ if (carOnOppositeSide != null
+ && !costSharedCars.containsKey(carOnOppositeSide.getId())) {
+ costSharedCars.put(carOnOppositeSide.getId(), carOnOppositeSide);
+ sendUpdatesToCar(carOnOppositeSide);
+ }
+
+ Pair elemAheadAux = new Pair<>(elementAhead.getFirst(), elementAhead.getSecond());
+ GeoCar carAhead = null;
+ if (elemAheadAux != null && elemAheadAux.getFirst()!= null && elemAheadAux.getFirst() instanceof GeoCar
+ && !costSharedCars.containsKey(elemAheadAux.getFirst().getId())
+ && elemAheadAux.getSecond() < NetworkWiFi.maxWifiRange / 10000) {
+ costSharedCars.put(elemAheadAux.getFirst().getId(), (GeoCar) elemAheadAux.getFirst());
+ carAhead = (GeoCar) elementAhead.getFirst();
+ sendUpdatesToCar(carAhead);
+ }
+ }
+
+
+
/**
* Prepares the next position the car will go to. This must be called after the
* updateSpeed method.
*/
public MapPoint getNextPosition() {
+
+/* this boolean is set to "true" in SimulationEngine (following a fair strategy,
+ each car has its chance for route recalculation) */
+ if (turnToUpdateRoute && Globals.dynamicRoutes) {
+ updateGeoCarRoute();
+ this.turnToUpdateRoute = false;
+ }
+
GeoCarRoute route = this.routes.get(0);
MapPoint newPos = null;
nodesToMoveOver = new LinkedList();
@@ -398,6 +614,7 @@ public MapPoint getNextPosition() {
if (stoppedAtTrafficLight) {
hasMoved.set(true);
+// System.out.println("return current position");
return this.getCurrentPos();
}
@@ -411,7 +628,9 @@ public MapPoint getNextPosition() {
double avgFuelConsumption = ComputeAverageFuelConsumption.computeAverageFuelConsumption(routeFuelFromStart,
timei);
routesTime.append((routes_idx++) + " " + timei + " " + avgSpeed + " " + avgFuelConsumption + System.lineSeparator());
+// System.out.println("Reached destination: " + "id: " + this.getId() + " avgSpeed: " + avgSpeed);
}
+// System.out.println("null from 1 if");
return null;
}
@@ -419,6 +638,9 @@ public MapPoint getNextPosition() {
Node prevNode = null;
Node nextNode = mobility.getSegmentByIndex(this.getCurrentPos());
int newDirection = this.getCurrentPos().direction;
+
+// System.out.println("Intersection List size: " + route.getIntersectionList().size());
+// System.out.println("id: " + nextNode.id + " way id: " + nextNode.wayId);
while (it.hasNext()) {
prevNode = nextNode;
nextNode = it.next();
@@ -430,6 +652,7 @@ public MapPoint getNextPosition() {
* TODO(mariana): e ceva gresit cu ruta asta, returnam null ca apoi sa i se faca
* skip
*/
+// System.out.println("null from mariana if");
return null;
}
@@ -492,6 +715,7 @@ public MapPoint getNextPosition() {
double avgFuelConsumption = ComputeAverageFuelConsumption.computeAverageFuelConsumption(routeFuelFromStart,
timei);
routesTime.append((routes_idx++) + " " + timei + " " + avgSpeed + " " + avgFuelConsumption + System.lineSeparator());
+
}
return null;
}
@@ -515,7 +739,6 @@ public void prepareMove() {
return;
}
if (this.getCurrentPos() == null) {
- // System.out.println("begin new route");
setBeginNewRoute(true);
initRoute();
return;
@@ -523,19 +746,22 @@ public void prepareMove() {
elementAhead = mobility.getElementAhead(this, driver.getInfluenceDistance(speed));
+ if (Globals.costSharingApps && streetsCostSharing.getStreetUpdates().size() > 0)
+ checkForNeighboursForCostSharing();
+
oldSpeed = speed;
updateSpeed();
- // System.out.println("speed: " + speed);
- // System.out.println("way: " + this.getCurrentPos().wayId);
nextPos = getNextPosition();
this.setBeginNewRoute(false);
+
mobility.queueNextMove(this.getId(), nextPos);
hasMoved.set(false);
} catch (RuntimeException e) {
/** Something was wrong with the route, so it's better to start a new one. */
+
setBeginNewRoute(true);
// long timei = SimulationEngine.getInstance().getSimulationTime() -
// routeStartTime;
@@ -584,6 +810,7 @@ public void move() {
return;
}
if (newPosition == this.getCurrentPos()) {
+
if (still > 200) {
/* we've stayed too much time in the same place */
resetRoute();
@@ -611,10 +838,16 @@ public void move() {
mobility.removeQueuedCar(oldNextPos, this.getId());
this.setCurrentPos(newPosition);
mobility.addCar(this);
+
+// update the elements before dropping relevant data
+ if (Globals.dynamicRoutes)
+ updateCostRelatedElements();
+
finishMove();
}
} catch (RuntimeException e) {
/** Something was wrong with the route, so it's better to start a new one. */
+
setBeginNewRoute(true);
initRoute();
return;
@@ -688,11 +921,19 @@ public void initStartPosition() {
// tracesTime.append("< " + start.lat + " " + start.lon + " " + end.lat + " " +
// end.lon + " " + (start.timestamp.getTime() - end.timestamp.getTime()) / 1000
// + "\n");
+
+ /* set the way of the first joint Node and set starting time in order to
+ start current way cost*/
+ currentWay = mobility.streetsGraph.get(first.wayId);
+
+ this.startingNode = mobility.getSegmentByIndex(route.getStartPoint());
+ this.wayStartTime = SimulationEngine.getInstance().getSimulationTime();
}
public void printRouteData(String filename) {
try {
String city = SimulationEngine.getInstance().getMapConfig().getCity();
+ System.out.println("SE SCRIEEE!!!!!!!!!!!!!!!!!! " + filename);
city += "/";
if (Globals.useTrafficLights)
@@ -719,6 +960,9 @@ public void printRouteData(String filename) {
}
public void initRoute() {
+ updatedRoute = false;
+ costSharedTrafficLight.clear();
+
try {
if (isBeginNewRoute()) {
resetRoute();
@@ -759,11 +1003,15 @@ public void initRoute() {
this.routes.add(route);
}
hasMoved.set(true);
+
+ entireRouteCost = this.streetsCostSharing.calculateEntireRouteCost(this.getCurrentRoute().getWayIdsSet());
+
} catch (RuntimeException e) {
this.setCurrentPos(null);
this.routes.remove(0);
hasMoved.set(true);
}
+
}
/**
@@ -786,6 +1034,7 @@ public void resetRoute() {
* Destroy the next position.
*/
public void finishMove() {
+
nodesToMoveOver = null;
nextPos = null;
}
diff --git a/src/model/GeoCarRoute.java b/src/model/GeoCarRoute.java
index 0214632..ce0f590 100644
--- a/src/model/GeoCarRoute.java
+++ b/src/model/GeoCarRoute.java
@@ -2,6 +2,7 @@
import java.io.Serializable;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
import model.OSMgraph.Node;
@@ -13,12 +14,26 @@ public class GeoCarRoute implements Serializable{
private MapPoint endPoint;
private List intersectionList;
private List originalintersectionList;
-
+ private HashSet wayIdsSet;
+
+ public HashSet getWayIdsSet() {
+ return wayIdsSet;
+ }
+
+ public void setWayIdsSet(HashSet wayIdsSet) {
+ this.wayIdsSet = wayIdsSet;
+ }
+
public GeoCarRoute(MapPoint start, MapPoint end, List intersections) {
startPoint = start;
endPoint = end;
intersectionList = intersections;
originalintersectionList = new ArrayList(intersections);
+
+ this.wayIdsSet = new HashSet();
+ for (int i = 0; i < intersectionList.size(); i++) {
+ wayIdsSet.add(intersectionList.get(i).wayId);
+ }
}
public MapPoint getStartPoint() {
diff --git a/src/model/OSMgraph/Way.java b/src/model/OSMgraph/Way.java
index 6b7947e..8c24d6a 100644
--- a/src/model/OSMgraph/Way.java
+++ b/src/model/OSMgraph/Way.java
@@ -74,6 +74,7 @@ public Way(long id) {
max_long = -360;
}
+
/** It is oneway or not */
public void setDirection(boolean val) {
oneway = val;
diff --git a/src/model/mobility/MobilityEngine.java b/src/model/mobility/MobilityEngine.java
index d558849..89ea020 100644
--- a/src/model/mobility/MobilityEngine.java
+++ b/src/model/mobility/MobilityEngine.java
@@ -1,5 +1,6 @@
package model.mobility;
+import java.awt.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
@@ -11,6 +12,8 @@
import application.routing.RoutingApplicationData.RoutingApplicationState;
import application.routing.RoutingApplicationParameters;
import application.routing.RoutingRoadCost;
+import controller.network.NetworkWiFi;
+import gui.CarView;
import model.Entity;
import model.GeoCar;
import model.GeoCarRoute;
@@ -388,7 +391,13 @@ public int getSegmentForPointNotOnSegment(MapPoint point) {
else
return way.getNodeIndex(around.getSecond().id);
}
- public static void initDataDijkstraQ( TreeMap graph, Node startNode, TreeMap,Node> path, TreeMap,Double> distance, int depthMax ){
+
+ /*
+ this function explores the graph in a BFS manner and adds into the "path" TreeMap
+ the nodes that could be visited, but being constrained by a maximum depth
+ Path has pairs of (wayId, nodeId) as keys and Nodes as values (nodes with default value - Node(-1, -1, -1))
+ */
+ public static void initDataDijkstraQ( TreeMap graph, Node startNode, TreeMap,Node> path, TreeMap,Double> distance, int depthMax ){
int level = 0;
LinkedList> q = new LinkedList>();
@@ -482,7 +491,8 @@ public void initDataDijkstra( TreeMap streetsGraph, Node startNode, T
initDataDijkstra(streetsGraph, jointNode, path, distance, depthMax - 1 );
}
}
-
+
+
/**
* FindPath - returns a list with the intersections that have to passed.
* @param graph - the street graphs
@@ -693,9 +703,71 @@ public boolean isIntersectionAhead(GeoCar car, double distance, Node intersectio
}
+ public GeoCar getCarOnOppositeSide(GeoCar car) {
+ MapPoint currentPos = car.getCurrentPos();
+ GeoCarRoute route = car.getCurrentRoute();
+ Way way = streetsGraph.get(currentPos.wayId);
+ Node currentNode = way.nodes.get(currentPos.segmentIndex);
+ Entity carOnTheOtherSide;
+ Long crtCell = currentPos.cellIndex;
+ int direction, queueNr;
+ double dist;
+ Iterator it = route.getIntersectionList().iterator();
+ Node next = null;
+
+ if (way.oneway) return null;
+
+ while (it.hasNext()) {
+ next = it.next();
+ dist = TraceParsingTool.distance(currentPos.lat, currentPos.lon, next.lat, next.lon);
+
+ /** check if the next node is still on the same way.
+ * check if the distance between nodes does not exceed the max wifi range*/
+
+ if (currentNode.wayId != next.wayId
+ || dist > NetworkWiFi.maxWifiRange / 10000) {
+ break;
+ }
+ direction = getDirection(currentNode, next);
+
+ /*select the opposite direction.*/
+ queueNr = (direction == 1) ? 1 : 0;
+
+ int segmentIndex = way.getNodeIndex(currentNode.id);
+
+ /*segment index remains the same because the segments have the same order
+ * in both directions*/
+ int queueSegmentIndex = (direction == 1) ?
+ segmentIndex :
+ way.nodes.size() - 1 - segmentIndex;
+
+ TreeMap segmentQueue = way.streetQueues[queueNr].get(queueSegmentIndex);
+
+
+ if (segmentQueue.size() != 0) {
+ Cell cell = segmentQueue.firstEntry().getValue();
+ carOnTheOtherSide = SimulationEngine.getInstance().
+ entities.get(cell.trafficEntityId);
+ if (carOnTheOtherSide.getId() == car.getId()) {
+ return null;
+ }
+
+ double distAhead = TraceParsingTool.distance(car.getCurrentPos().lat, car.getCurrentPos().lon,
+ carOnTheOtherSide.getCurrentPos().lat, carOnTheOtherSide.getCurrentPos().lon);
+
+ if (distAhead > NetworkWiFi.maxWifiRange / 10000) {
+ return null;
+ }
+
+ return (GeoCar) carOnTheOtherSide;
+ }
+ }
+ return null;
+ }
+
/**
* Returns the car in front of the given car, within a given distance.
- *
+ *
* @param car
* @param distance
* @return null if it's the end of the route
@@ -716,22 +788,22 @@ public Pair getElementAhead(GeoCar car, double distance) {
if (crtPos.equals(route.getEndPoint()))
return null;
-
+
int queueNr = (crtPos.direction == 1) ? 0 : 1;
-
+
Iterator it = route.getIntersectionList().iterator();
Node next = null;
while (it.hasNext()) {
next = it.next();
-
+
/* Check if the next node has a traffic light control */
if (next.hasTrafficLightControl()) {
elementAhead = SimulationEngine.getInstance().entities.get(next.getTrafficLightId());
distAhead += TraceParsingTool.distance(lat, lon,
elementAhead.getCurrentPos().lat, elementAhead.getCurrentPos().lon);
- return new Pair(elementAhead, distAhead);
+ return new Pair(elementAhead, distAhead);
}
-
+
if (crt.wayId != next.wayId) {
crt = getSegmentById(next.wayId, crt.id);
/**
@@ -742,52 +814,52 @@ public Pair getElementAhead(GeoCar car, double distance) {
return null;
}
way = streetsGraph.get(crt.wayId);
-
+
/**
- * TODO(Cosmin): Check this condition because it
+ * TODO(Cosmin): Check this condition because it
* seems not to work properly due to the fact that
* one street can have a joint node in the middle of
* the nodes vector. !!!
- */
+ */
int direction = getDirection(crt, next);
queueNr = (direction == 1) ? 0 : 1;
-
+
int segmentIndex = way.getNodeIndex(crt.id);
int queueSegmentIndex = (direction == 1) ? segmentIndex :
way.nodes.size() - 1 - segmentIndex;
-
+
TreeMap segmentQueue = way.streetQueues[queueNr].get(queueSegmentIndex);
-
+
/* On the first segment, remove cars behind current car */
if (way.id == crtPos.wayId && segmentIndex == crtPos.segmentIndex) {
TreeMap aux = new TreeMap(segmentQueue);
segmentQueue = new TreeMap(aux.tailMap(crtCell));
segmentQueue.remove(crtCell);
}
-
+
if (segmentQueue.size() != 0) {
Cell cell = segmentQueue.firstEntry().getValue();
elementAhead = SimulationEngine.getInstance().
- entities.get(cell.trafficEntityId);
-
+ entities.get(cell.trafficEntityId);
+
distAhead += TraceParsingTool.distance(lat, lon,
elementAhead.getCurrentPos().lat, elementAhead.getCurrentPos().lon);
return new Pair(elementAhead, distAhead);
}
-
+
/* No car on this segment, move to the next one. */
double dist = TraceParsingTool.distance(lat, lon, crt.lat, crt.lon);
if (dist > distance)
return new Pair(null, null);
distance -= dist;
distAhead += dist;
-
+
lat = crt.lat;
lon = crt.lon;
crt = next;
}
-
+
return new Pair(null, null);
}
diff --git a/src/model/network/Message.java b/src/model/network/Message.java
index 6651deb..cb02b4c 100644
--- a/src/model/network/Message.java
+++ b/src/model/network/Message.java
@@ -21,10 +21,18 @@ public class Message extends Header {
private ApplicationType appType;
private MessageType type;
private Integer priority;
+
+ private int messageDirection;
private byte[] payload;
-
-
+
+ public int getMessageDirection() {
+ return messageDirection;
+ }
+
+ public void setMessageDirection(int messageDirection) {
+ this.messageDirection = messageDirection;
+ }
public Message(long sourceId, long destId, Object data, MessageType type, ApplicationType appType) {
this.setSourceId(sourceId);
@@ -32,6 +40,7 @@ public Message(long sourceId, long destId, Object data, MessageType type, Applic
this.setData(data);
this.setType(type);
this.setAppType(appType);
+ this.messageDirection = 1;
}
diff --git a/src/model/network/MessageType.java b/src/model/network/MessageType.java
index 4007fdb..28c984a 100644
--- a/src/model/network/MessageType.java
+++ b/src/model/network/MessageType.java
@@ -17,5 +17,9 @@ public enum MessageType {
ADD_WAITING_QUEUE,
REMOVE_WAITING_QUEUE,
/* Used by SincronizeIntersectionsApplication */
- SYNCHRONIZE_TRAFFIC_LIGHTS
+ SYNCHRONIZE_TRAFFIC_LIGHTS,
+ /* Used by Dynamic Routing Apps */
+ CAR_INFORMS,
+ TRAFFIC_LIGHT_INFORMS,
+ OUTDATED_COSTS
}
\ No newline at end of file
diff --git a/src/model/network/SourceType.java b/src/model/network/SourceType.java
new file mode 100644
index 0000000..56dd7f1
--- /dev/null
+++ b/src/model/network/SourceType.java
@@ -0,0 +1,6 @@
+package model.network;
+
+public enum SourceType {
+ CAR,
+ TRAFFIC_LIGHT
+}
diff --git a/src/model/parameters/Globals.java b/src/model/parameters/Globals.java
index 048fbcf..0df4782 100644
--- a/src/model/parameters/Globals.java
+++ b/src/model/parameters/Globals.java
@@ -8,6 +8,7 @@
import com.beust.jcommander.Parameter;
import controller.network.NetworkType;
import controller.network.NetworkUtils;
+import model.personality.PersonalityTypes;
/**
* Contains command line parameters and static fields that are used in multiple
@@ -45,9 +46,25 @@ public class Globals {
@Parameter(names = {"--debug"}, description = "The length of a clock tick.")
public static int debug = 0;
-
+
+ /* Malicious Behavior */
@Parameter(names = {"--carsCount"}, description = "The number of cars simulated.")
- public static int carsCount = 150;
+ public static int carsCount = 300;
+
+ @Parameter(names = {"--maliciousCars"}, description = "the number of malicious cars")
+ public static int maliciousCars = 0;
+
+ public static PersonalityTypes maliciousPersonalityType = PersonalityTypes.MALICIOUS_RANDOM;
+
+ @Parameter(names = {"--dynamicRoutes"}, description = "The cars change their routes dynamically")
+ public static boolean dynamicRoutes = false;
+
+ @Parameter(names = {"--CostSharingApps"}, description = "use the apps for streets cost sharing")
+ public static boolean costSharingApps = false;
+
+ @Parameter(names = {"--votingSystem"}, description = "activate the voting system for cars before route recalculation")
+ public static boolean useVotingSystem = false;
+ /* end MB*/
@Parameter(names = {"--maxWaitingTime"}, description = "The maximum simulation time a car can wait at a traffic light.")
public static int maxWaitingTime = 120;
@@ -68,7 +85,7 @@ public class Globals {
public static boolean loadGraph = false;
@Parameter(names = {"--simulationDays"}, description = "Duration of the simulation in days.")
- public static int simulationDays = 7;
+ public static int simulationDays = 1;
@Parameter(names = {"--randomCarsSelect"}, description = "Set the percentage of cars to use in the simulation: 0.0 == none, 1.0 = all.")
public static double randomCarsSelect = 1.0;
@@ -140,7 +157,7 @@ public class Globals {
@Parameter(names = {"--activeApps"}, description = "the accepted values ROUTING,TILES,STREET_VISITS,TRAFFIC_LIGHT_CONTROL."
+ "Please see ApplicationType for more details\n."
+ "Multiple applications can be passed using --activeApps=app1,app2,app3,..,appn")
- public static String activeApps = "ROUTING,TRAFFIC_LIGHT_CONTROL";
+ public static String activeApps = "TRAFFIC_LIGHT_CONTROL";
/* The default application is ROUTING_APP */
public static Vector activeApplications;
diff --git a/src/model/personality/PersonalityTypes.java b/src/model/personality/PersonalityTypes.java
new file mode 100644
index 0000000..23c90d2
--- /dev/null
+++ b/src/model/personality/PersonalityTypes.java
@@ -0,0 +1,8 @@
+package model.personality;
+
+public enum PersonalityTypes {
+ REGULAR,
+ MALICIOUS_RANDOM,
+ MALICIOUS_INCREASED,
+ MALICIOUS_DECREASED
+}
diff --git a/src/utils/StreetsCostSharing.java b/src/utils/StreetsCostSharing.java
new file mode 100644
index 0000000..4c91917
--- /dev/null
+++ b/src/utils/StreetsCostSharing.java
@@ -0,0 +1,267 @@
+package utils;
+import application.dynamic_routing.VotingStreetCostData;
+import model.GeoCar;
+import model.OSMgraph.Way;
+import model.OSMgraph.Node;
+import model.mobility.MobilityEngine;
+import model.parameters.Globals;
+import utils.tracestool.Utils;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * This class has a batch of functions useful for dynamic routes calculations.
+ * Also, it has a reference to car's hashmap of streets cost (each car
+ * has its own perspective about the streets costs)
+ *
+ */
+
+public class StreetsCostSharing {
+
+// pairs between each way id and its current cost
+// each car has its representation
+ protected HashMap streetGraphCost;
+
+ protected HashMap streetUpdates;
+ public static int MAX_DYNAMIC_VOTING_THRESHOLD = 7;
+ public static int MIN_DYNAMIC_VOTING_THRESHOLD = 2;
+
+ // percentage threshold for considering a street cost updated
+ public static double STREET_COST_UPDATE_THRESHOLD = 0.4;
+
+ /* street update constraints: CAR_MIN_TRACE_ON_STREET - percent
+ * CAR_MIN_TIME_ON_STREET - timestamps*/
+ public static double CAR_MIN_TRACE_ON_STREET = 0.5;
+ public static double CAR_MIN_TIME_ON_STREET = 10;
+
+ MobilityEngine mobilityEngine;
+ GeoCar car;
+
+
+ public HashMap getStreetUpdates() {
+ return streetUpdates;
+ }
+
+ public StreetsCostSharing(GeoCar car){
+ this.mobilityEngine = MobilityEngine.getInstance();
+ streetGraphCost = new HashMap();
+ this.streetUpdates = new HashMap();
+ this.car = car;
+ }
+
+ /* addNewUpdate and removeOutdatedInfo are functions for
+ * handling the updates regarding streets' costs by adding new costs
+ * or removing a info that already has been scattered*/
+
+ public void addNewUpdate(Long wayId, Double cost, Long discovererStamp) {
+
+ if (!this.streetUpdates.containsKey(wayId)) {
+ this.streetUpdates.put(wayId, new VotingStreetCostData(cost, this.car.getId()));
+ return;
+ }
+
+ VotingStreetCostData votingStreetCostData = this.streetUpdates.get(wayId);
+ votingStreetCostData.addDiscovererStamp(cost, discovererStamp);
+ }
+
+ /*The network is aware of this street costs. So they can be deleted from the
+ * sharing package (streetUpdates).
+ * There is no point in trying to achieve a number of votes for this cost. Given the fact
+ * that the network is aware of it, the car will receive a valid package in the near future*/
+ public void removeOutdatedInfo(HashMap outdatedCosts) {
+ Iterator itr = outdatedCosts.keySet().iterator();
+ long currentWayId;
+ double currentUpdateCost;
+ double outdatedCost;
+
+ while (itr.hasNext()) {
+ currentWayId = itr.next();
+
+ currentUpdateCost = streetUpdates.get(currentWayId).getStreetCost();
+ outdatedCost = outdatedCosts.get(currentWayId).getStreetCost();
+
+ /*check if the currentUpdateCost has been modified before receiving this update
+ * regarding outdated cost. So, if the difference between this 2 costs is under THRESHOLD,
+ * there has been an update over this cost so it is not outdated anymore*/
+ if (Math.abs(currentUpdateCost - outdatedCost) <
+ (currentUpdateCost * StreetsCostSharing.STREET_COST_UPDATE_THRESHOLD)) {
+ streetUpdates.remove(currentWayId);
+ }
+ }
+ }
+
+/* returns the cost of street if it knows, otherwise calculate*/
+ public double getWayCost(Way way) {
+ double streetCost;
+
+ /*if the car does not know the street cost, take the first and the last node
+ * of the street. Calculate the distance between them using normal cruising speed.*/
+ if (!streetGraphCost.containsKey(way.id)) {
+ Node firstNode = way.nodes.firstElement();
+ Node lastNode = way.nodes.lastElement();
+
+ double streetLength = Utils.getRealDistanceAB(way, firstNode.id, lastNode.id);
+ streetCost = streetLength / (way.getMaximumSpeed() - 3);
+ streetGraphCost.put(way.id, streetCost);
+
+ } else {
+ streetCost = streetGraphCost.get(way.id);
+ }
+ return streetCost;
+ }
+
+
+ /* this function calculates the average street cost considering a car that traveled just a segment
+ * of the entire street. We cannot assume that the distance traveled by the car is the
+ * actual street length*/
+ public double calculateAverageCost (Way way, Node startingNode, Node finishNode, long wayStartTime, long wayFinishTime) {
+ Node firstNode = way.nodes.firstElement();
+ Node lastNode = way.nodes.lastElement();
+ long carTraceTime = wayFinishTime - wayStartTime;
+
+ double streetLength = Utils.getRealDistanceAB(way, firstNode.id, lastNode.id);
+
+// car's trace on the current street
+ double carTraceLength = Utils.getRealDistanceAB(way, startingNode.id, finishNode.id);
+
+// start node is not correct
+ if (carTraceLength > streetLength) {
+ return -1;
+ }
+
+ if (carTraceLength < (StreetsCostSharing.CAR_MIN_TRACE_ON_STREET * streetLength) ||
+ carTraceTime < StreetsCostSharing.CAR_MIN_TIME_ON_STREET) {
+ return -1;
+ }
+ double speed = carTraceLength / carTraceTime;
+
+ if (speed == 0) {
+ return -1;
+ }
+
+ return streetLength / speed;
+ }
+
+
+ /* this method is called when a car changes the street. So it is able to generate a report */
+ public void discoverNewWayCost(Way way, Node startingNode, Node finishNode, long startTime, long finishTime) {
+
+ double reportedCost = calculateAverageCost(way, startingNode, finishNode, startTime, finishTime);
+
+ if (reportedCost == -1) {
+ return;
+ }
+
+ double currentStreetCost = getWayCost(way);
+
+// use the percentage threshold to validate the new update
+ if (Math.abs(currentStreetCost - reportedCost) >
+ (currentStreetCost * StreetsCostSharing.STREET_COST_UPDATE_THRESHOLD)) {
+ streetGraphCost.put(way.id, reportedCost);
+
+ // add this new cost to the "StreetUpdates" in order to inform the next trafficLight
+ this.addNewUpdate(way.id, reportedCost, this.car.getId());
+ }
+
+ }
+
+ public static long counterMaliciousCosts = 0;
+ public static long counterRealCosts = 0;
+
+ /*function called when a the CarDynamicRoutingApp finds some validUpdates for this car*/
+ public void updateWayCost(long wayId, VotingStreetCostData receivedVotingData) {
+
+ /*stamps management*/
+ if (Globals.useVotingSystem) {
+ if (!this.streetUpdates.containsKey(wayId)) {
+ this.streetUpdates.put(wayId, receivedVotingData);
+ } else {
+
+ VotingStreetCostData currentVotingData = this.streetUpdates.get(wayId);
+ currentVotingData.addNewStamps(receivedVotingData);
+ }
+
+ VotingStreetCostData votingStreetCostData = this.streetUpdates.get(wayId);
+ if (votingStreetCostData.isStreetCostUpdated()) {
+
+ if (car.getCurrentRoute().getWayIdsSet().contains(wayId)) {
+
+ if (VotingStreetCostData.isTheSameStreetCost(receivedVotingData.getStreetCost(), StreetsCostSharingMalicious.DECREASED_VALUE)
+ || (VotingStreetCostData.isTheSameStreetCost(receivedVotingData.getStreetCost(), StreetsCostSharingMalicious.INCREASED_VALUE))) {
+ synchronized (StreetsCostSharing.class) {
+ counterMaliciousCosts++;
+ }
+ } else {
+ synchronized (StreetsCostSharing.class) {
+ counterRealCosts++;
+ }
+ }
+
+ if (receivedVotingData.getStreetCost() > this.getWayCost(mobilityEngine.getWay(wayId))) {
+ double oldCost = this.getWayCost(mobilityEngine.getWay(wayId));
+ this.streetGraphCost.put(wayId, receivedVotingData.getStreetCost());
+ this.streetUpdates.put(wayId, receivedVotingData);
+
+ car.appointForRouteRecalculation( receivedVotingData.getStreetCost()
+ - oldCost);
+ }
+ }
+
+ // clear the voting session and redistribute the new info
+ votingStreetCostData.clearVoteSession();
+ this.streetUpdates.put(wayId, votingStreetCostData);
+
+ /* the cost was voted, so we can add the cost to the KB*/
+ this.streetGraphCost.put(wayId, votingStreetCostData.getStreetCost());
+
+ } else {
+ /*otherwise, we have to wait until the cost is voted*/
+ }
+ } else {
+ // do not use voting system
+
+ if (car.getCurrentRoute().getWayIdsSet().contains(wayId)) {
+
+ if (VotingStreetCostData.isTheSameStreetCost(receivedVotingData.getStreetCost(), StreetsCostSharingMalicious.DECREASED_VALUE)
+ || (VotingStreetCostData.isTheSameStreetCost(receivedVotingData.getStreetCost(), StreetsCostSharingMalicious.INCREASED_VALUE))) {
+ synchronized (StreetsCostSharing.class) {
+ counterMaliciousCosts++;
+ }
+ } else {
+ synchronized (StreetsCostSharing.class) {
+ counterRealCosts++;
+ }
+ }
+
+ if (receivedVotingData.getStreetCost() > this.getWayCost(mobilityEngine.getWay(wayId))) {
+ double oldCost = this.getWayCost(mobilityEngine.getWay(wayId));
+ this.streetGraphCost.put(wayId, receivedVotingData.getStreetCost());
+ this.streetUpdates.put(wayId, receivedVotingData);
+
+ car.appointForRouteRecalculation( receivedVotingData.getStreetCost()
+ - oldCost);
+ }
+ } else {
+ this.streetGraphCost.put(wayId, receivedVotingData.getStreetCost());
+ this.streetUpdates.put(wayId, receivedVotingData);
+ }
+ }
+
+ }
+
+ public double calculateEntireRouteCost(HashSet wayIdsSet) {
+ Iterator it = wayIdsSet.iterator();
+ long currentWayId = 0;
+ double total = 0;
+ double currentCost = 0;
+
+ while (it.hasNext()) {
+ currentWayId = it.next();
+ currentCost = this.getWayCost(mobilityEngine.getWay(currentWayId));
+ total += currentCost;
+ }
+ return total;
+ }
+}
diff --git a/src/utils/StreetsCostSharingMalicious.java b/src/utils/StreetsCostSharingMalicious.java
new file mode 100644
index 0000000..1798759
--- /dev/null
+++ b/src/utils/StreetsCostSharingMalicious.java
@@ -0,0 +1,77 @@
+package utils;
+
+import model.GeoCar;
+import model.OSMgraph.Node;
+import model.OSMgraph.Way;
+import model.mobility.MobilityEngine;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+public class StreetsCostSharingMalicious extends StreetsCostSharing {
+
+ public static final double INCREASED_VALUE = 4000;
+ public static final double DECREASED_VALUE = 1;
+ public static final int REPORT_MULTIPLICATION_FACTOR = 10;
+ ArrayList wayIdArr;
+
+ public StreetsCostSharingMalicious(GeoCar car) {
+
+ super(car);
+ wayIdArr = new ArrayList(MobilityEngine.getInstance().streetsGraph.keySet());
+
+ }
+
+ @Override
+ public void discoverNewWayCost(Way way, Node startingNode, Node finishNode, long startTime, long finishTime) {
+ int counter = 0;
+ Random rand = new Random();
+ long wayId;
+
+ switch (super.car.getPersonalityType()) {
+ case MALICIOUS_RANDOM:
+
+ while (counter < REPORT_MULTIPLICATION_FACTOR) {
+ int position = rand.nextInt(wayIdArr.size());
+ wayId = wayIdArr.get(position);
+
+ if (rand.nextInt(10) % 2 == 0)
+ super.addNewUpdate(wayId, StreetsCostSharingMalicious.INCREASED_VALUE, super.car.getId());
+ else
+ super.addNewUpdate(wayId, StreetsCostSharingMalicious.DECREASED_VALUE, super.car.getId());
+ counter++;
+ }
+
+ if (rand.nextInt(10) % 2 == 0) {
+ super.addNewUpdate(way.id, StreetsCostSharingMalicious.INCREASED_VALUE, super.car.getId());
+ } else {
+ super.addNewUpdate(way.id, StreetsCostSharingMalicious.DECREASED_VALUE, super.car.getId());
+ }
+ break;
+
+ case MALICIOUS_INCREASED:
+
+ while (counter < REPORT_MULTIPLICATION_FACTOR) {
+ int position = rand.nextInt(wayIdArr.size());
+ wayId = wayIdArr.get(position);
+ super.addNewUpdate(wayId, StreetsCostSharingMalicious.INCREASED_VALUE, super.car.getId());
+ counter++;
+ }
+ super.addNewUpdate(way.id, StreetsCostSharingMalicious.INCREASED_VALUE, super.car.getId());
+ break;
+
+ case MALICIOUS_DECREASED:
+
+ while (counter < REPORT_MULTIPLICATION_FACTOR) {
+ int position = rand.nextInt(wayIdArr.size());
+ wayId = wayIdArr.get(position);
+ super.addNewUpdate(wayId, StreetsCostSharingMalicious.DECREASED_VALUE, super.car.getId());
+ counter++;
+ }
+ super.addNewUpdate(way.id, StreetsCostSharingMalicious.DECREASED_VALUE, super.car.getId());
+ break;
+ }
+
+ }
+
+}