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; + } + + } + +}