mirror of
				https://github.com/Ahp06/SUMO_Emissions.git
				synced 2025-10-30 01:49:19 +00:00 
			
		
		
		
	Added docstrings to all methods of the application
This commit is contained in:
		| @@ -3,26 +3,37 @@ Created on 17 oct. 2018 | |||||||
|  |  | ||||||
| @author: Axel Huynh-Phuc, Thibaud Gasser | @author: Axel Huynh-Phuc, Thibaud Gasser | ||||||
| """ | """ | ||||||
| import traci |  | ||||||
| from traci._trafficlight import Logic |  | ||||||
| from typing import Iterable | from typing import Iterable | ||||||
|  |  | ||||||
| from shapely.geometry.linestring import LineString | import traci | ||||||
|  |  | ||||||
| from model import Area, Vehicle | from model import Area, Vehicle | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | This module defines all possible actions on the simulation | ||||||
|  | """ | ||||||
|  |  | ||||||
| def compute_edge_weight(edge_id): | def compute_edge_weight(edge_id): | ||||||
|      |     """ | ||||||
|  |     Sum the different pollutant emissions on the edge with the identifier edge_id | ||||||
|  |     :param edge_id: The edge ID | ||||||
|  |     :return: The sum (in mg) of all pollutant emissions | ||||||
|  |     """ | ||||||
|     co2 = traci.edge.getCO2Emission(edge_id) |     co2 = traci.edge.getCO2Emission(edge_id) | ||||||
|     co = traci.edge.getCOEmission(edge_id) |     co = traci.edge.getCOEmission(edge_id) | ||||||
|     nox = traci.edge.getNOxEmission(edge_id) |     nox = traci.edge.getNOxEmission(edge_id) | ||||||
|     hc = traci.edge.getHCEmission(edge_id) |     hc = traci.edge.getHCEmission(edge_id) | ||||||
|     pmx = traci.edge.getPMxEmission(edge_id) |     pmx = traci.edge.getPMxEmission(edge_id) | ||||||
|  |  | ||||||
|     return (co2 + co + nox + hc + pmx) |     return co2 + co + nox + hc + pmx | ||||||
|  |  | ||||||
|  |  | ||||||
| def adjust_edges_weights(area): | def adjust_edges_weights(area): | ||||||
|  |     """ | ||||||
|  |     Changes the edge weight of all edges into the area | ||||||
|  |     :param area: The Area object | ||||||
|  |     :return: | ||||||
|  |     """ | ||||||
|     area.weight_adjusted = True |     area.weight_adjusted = True | ||||||
|     for lane in area._lanes: |     for lane in area._lanes: | ||||||
|         edge_id = traci.lane.getEdgeID(lane.lane_id) |         edge_id = traci.lane.getEdgeID(lane.lane_id) | ||||||
| @@ -32,54 +43,94 @@ def adjust_edges_weights(area): | |||||||
|     for veh_id in traci.vehicle.getIDList(): |     for veh_id in traci.vehicle.getIDList(): | ||||||
|         traci.vehicle.rerouteEffort(veh_id) |         traci.vehicle.rerouteEffort(veh_id) | ||||||
|  |  | ||||||
| def limit_speed_into_area(area: Area, vehicles: Iterable[Vehicle], speed_rf): |  | ||||||
|  | def limit_speed_into_area(area: Area, speed_rf): | ||||||
|  |     """ | ||||||
|  |     Limit the speed into the area by speed_rf factor | ||||||
|  |     :param area: The Area object | ||||||
|  |     :param speed_rf: The speed reduction factor (must be positive) | ||||||
|  |     :return: | ||||||
|  |     """ | ||||||
|     area.limited_speed = True |     area.limited_speed = True | ||||||
|     for lane in area._lanes: |     for lane in area._lanes: | ||||||
|         traci.lane.setMaxSpeed(lane.lane_id, speed_rf * lane.initial_max_speed) |         traci.lane.setMaxSpeed(lane.lane_id, speed_rf * lane.initial_max_speed) | ||||||
|  |  | ||||||
| def modifyLogic(logic, rf): #rf for "reduction factor"  |  | ||||||
|  | def modifyLogic(logic, rf): | ||||||
|  |     """ | ||||||
|  |     Change the logic of a traffic light by decreasing the overall duration of the traffic light | ||||||
|  |     :param logic: The Logic object | ||||||
|  |     :param rf: The reduction factor (must be positive) | ||||||
|  |     :return: A new Logic object with all phases modified | ||||||
|  |     """ | ||||||
|     new_phases = [] |     new_phases = [] | ||||||
|     for phase in logic._phases: |     for phase in logic._phases: | ||||||
|         new_phase = traci.trafficlight.Phase(phase.duration*rf,phase.minDuration*rf,phase.maxDuration*rf,phase.phaseDef) |         new_phase = traci.trafficlight.Phase(phase.duration * rf, phase.minDuration * rf, phase.maxDuration * rf, | ||||||
|  |                                              phase.phaseDef) | ||||||
|         new_phases.append(new_phase) |         new_phases.append(new_phase) | ||||||
|  |  | ||||||
|     return traci.trafficlight.Logic("new-program", 0 , 0 , 0 , new_phases)     |     return traci.trafficlight.Logic("new-program", 0, 0, 0, new_phases) | ||||||
|  |  | ||||||
|  |  | ||||||
| def adjust_traffic_light_phase_duration(area, reduction_factor): | def adjust_traffic_light_phase_duration(area, reduction_factor): | ||||||
|  |     """ | ||||||
|  |     Set all logics modification on traffic lights into the area | ||||||
|  |     :param area: The Area object | ||||||
|  |     :param reduction_factor: The reduction factor (must be positive) | ||||||
|  |     :return: | ||||||
|  |     """ | ||||||
|     area.tls_adjusted = True |     area.tls_adjusted = True | ||||||
|     for tl in area._tls: |     for tl in area._tls: | ||||||
|         for logic in tl._logics: |         for logic in tl._logics: | ||||||
|             traci.trafficlights.setCompleteRedYellowGreenDefinition(tl.tl_id, modifyLogic(logic,reduction_factor)) |             traci.trafficlights.setCompleteRedYellowGreenDefinition(tl.tl_id, modifyLogic(logic, reduction_factor)) | ||||||
|  |  | ||||||
|  |  | ||||||
| def count_vehicles_in_area(area): | def count_vehicles_in_area(area): | ||||||
|  |     """ | ||||||
|  |     Count the vehicles number into the area | ||||||
|  |     :param area: The Area object | ||||||
|  |     :return: The number of vehicles into the area | ||||||
|  |     """ | ||||||
|     vehicles_in_area = 0 |     vehicles_in_area = 0 | ||||||
|     for lane in area._lanes: |     for lane in area._lanes: | ||||||
|         vehicles_in_area += traci.lane.getLastStepVehicleNumber(lane.lane_id) |         vehicles_in_area += traci.lane.getLastStepVehicleNumber(lane.lane_id) | ||||||
|     return vehicles_in_area |     return vehicles_in_area | ||||||
|  |  | ||||||
|  |  | ||||||
| def lock_area(area): | def lock_area(area): | ||||||
|  |     """ | ||||||
|  |     Prohibits access to the area to a particular vehicle class | ||||||
|  |     NOT FIXED : Some vehicles continue to go into the area if they can not turn around and stay there | ||||||
|  |     :param area: The Area object | ||||||
|  |     :return: | ||||||
|  |     """ | ||||||
|     area.locked = True |     area.locked = True | ||||||
|     for lane in area._lanes: |     for lane in area._lanes: | ||||||
|  |         # The passenger class is an example, you have to adapt this code | ||||||
|         traci.lane.setDisallowed(lane.lane_id, 'passenger') |         traci.lane.setDisallowed(lane.lane_id, 'passenger') | ||||||
|  |  | ||||||
|  |  | ||||||
| def reverse_actions(area): | def reverse_actions(area): | ||||||
|     #Reset max speed to original  |     """ | ||||||
|  |     Reverse all actions made in an area | ||||||
|  |     :param area: The Area object | ||||||
|  |     :return: | ||||||
|  |     """ | ||||||
|  |     # Reset max speed to original | ||||||
|     if area.limited_speed: |     if area.limited_speed: | ||||||
|         area.limited_speed = False |         area.limited_speed = False | ||||||
|         for lane in area._lanes: |         for lane in area._lanes: | ||||||
|             traci.lane.setMaxSpeed(lane.lane_id, lane.initial_max_speed) |             traci.lane.setMaxSpeed(lane.lane_id, lane.initial_max_speed) | ||||||
|  |  | ||||||
|     #Reset traffic lights initial duration   |     # Reset traffic lights initial duration | ||||||
|     if area.tls_adjusted: |     if area.tls_adjusted: | ||||||
|         area.tls_adjusted = False |         area.tls_adjusted = False | ||||||
|         for tl in area._tls: |         for tl in area._tls: | ||||||
|             for initial_logic in tl._logics: |             for initial_logic in tl._logics: | ||||||
|                 traci.trafficlights.setCompleteRedYellowGreenDefinition(tl.tl_id, initial_logic._logic) |                 traci.trafficlights.setCompleteRedYellowGreenDefinition(tl.tl_id, initial_logic._logic) | ||||||
|  |  | ||||||
|     #Unlock the area  |     # Unlock the area | ||||||
|     if area.locked: |     if area.locked: | ||||||
|         area.locked = False |         area.locked = False | ||||||
|         for lane in area._lanes: |         for lane in area._lanes: | ||||||
|             traci.lane.setAllowed(lane.lane_id, '') #empty means all classes are allowed  |             traci.lane.setAllowed(lane.lane_id, '')  # empty means all classes are allowed | ||||||
|              |  | ||||||
|      |  | ||||||
|   | |||||||
| @@ -1,5 +1,7 @@ | |||||||
| """ | """ | ||||||
| Global configuration for the simulation | Created on 17 oct. 2018 | ||||||
|  |  | ||||||
|  | @author: Axel Huynh-Phuc, Thibaud Gasser | ||||||
| """ | """ | ||||||
|  |  | ||||||
| import datetime | import datetime | ||||||
| @@ -10,17 +12,32 @@ import sys | |||||||
|  |  | ||||||
| from model import Emission | from model import Emission | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | This module defines the global configuration for the simulation | ||||||
|  | """ | ||||||
|  |  | ||||||
|  |  | ||||||
| class Config: | class Config: | ||||||
|  |     """ | ||||||
|  |     The Config class defines all simulation properties that can be changed | ||||||
|  |     """ | ||||||
|  |  | ||||||
|     # Total of emissions of all pollutants in mg for n steps of simulation without acting on areas |     # Total of emissions of all pollutants in mg for n steps of simulation without acting on areas | ||||||
|     # These constants are simulation dependant, you must change them according to your simulation  |     # These constants are simulation dependant, you must change them according to your simulation  | ||||||
|     ref200 = Emission(co2=42816869.05436445, co=1128465.0343051048, nox=18389.648337283958, hc=6154.330914019103, |     ref200 = Emission(co2=42816869.05436445, co=1128465.0343051048, nox=18389.648337283958, hc=6154.330914019103, | ||||||
|                       pmx=885.0829265236318) |                       pmx=885.0829265236318) | ||||||
|  |  | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         """Default constructor""" |         """ | ||||||
|  |         Default constructor | ||||||
|  |         """ | ||||||
|  |  | ||||||
|     def import_config_file(self, config_file): |     def import_config_file(self, config_file): | ||||||
|  |         """ | ||||||
|  |         Import your configuration file in JSON format | ||||||
|  |         :param config_file: The path to your configuration file | ||||||
|  |         :return: | ||||||
|  |         """ | ||||||
|         with open(config_file, 'r') as f: |         with open(config_file, 'r') as f: | ||||||
|             data = json.load(f) |             data = json.load(f) | ||||||
|  |  | ||||||
| @@ -43,6 +60,10 @@ class Config: | |||||||
|         self.check_config() |         self.check_config() | ||||||
|  |  | ||||||
|     def check_config(self): |     def check_config(self): | ||||||
|  |         """ | ||||||
|  |         Check the relevance of user configuration choices | ||||||
|  |         :return: | ||||||
|  |         """ | ||||||
|         # Weight routing mode cannot be combinated with other actions |         # Weight routing mode cannot be combinated with other actions | ||||||
|         if self.weight_routing_mode: |         if self.weight_routing_mode: | ||||||
|             self.limit_speed_mode = False |             self.limit_speed_mode = False | ||||||
| @@ -57,6 +78,9 @@ class Config: | |||||||
|             self.lock_area_mode = False |             self.lock_area_mode = False | ||||||
|  |  | ||||||
|     def __repr__(self) -> str: |     def __repr__(self) -> str: | ||||||
|  |         """ | ||||||
|  |         :return: All properties chosen by the user | ||||||
|  |         """ | ||||||
|         return ( |         return ( | ||||||
|             f'grid : {self.areas_number}x{self.areas_number}\n' |             f'grid : {self.areas_number}x{self.areas_number}\n' | ||||||
|             f'step number = {self.n_steps}\n' |             f'step number = {self.n_steps}\n' | ||||||
| @@ -69,6 +93,10 @@ class Config: | |||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def init_traci(self): |     def init_traci(self): | ||||||
|  |         """ | ||||||
|  |         Init the Traci API | ||||||
|  |         :return: | ||||||
|  |         """ | ||||||
|         if 'SUMO_HOME' in os.environ: |         if 'SUMO_HOME' in os.environ: | ||||||
|             tools = os.path.join(os.environ['SUMO_HOME'], 'tools') |             tools = os.path.join(os.environ['SUMO_HOME'], 'tools') | ||||||
|             sys.path.append(tools) |             sys.path.append(tools) | ||||||
| @@ -79,6 +107,11 @@ class Config: | |||||||
|         self.sumo_cmd = [sumo_binary, "-c", self._SUMOCFG] |         self.sumo_cmd = [sumo_binary, "-c", self._SUMOCFG] | ||||||
|  |  | ||||||
|     def init_logger(self, save_logs=False): |     def init_logger(self, save_logs=False): | ||||||
|  |         """ | ||||||
|  |         Init the application logger | ||||||
|  |         :param save_logs: If save_logs is True, it will save the logs into the logs directory | ||||||
|  |         :return: | ||||||
|  |         """ | ||||||
|         now = datetime.datetime.now() |         now = datetime.datetime.now() | ||||||
|         current_date = now.strftime("%Y_%m_%d_%H_%M_%S") |         current_date = now.strftime("%Y_%m_%d_%H_%M_%S") | ||||||
|  |  | ||||||
| @@ -103,5 +136,8 @@ class Config: | |||||||
|         return logger |         return logger | ||||||
|  |  | ||||||
|     def get_ref_emissions(self): |     def get_ref_emissions(self): | ||||||
|  |         """ | ||||||
|  |         :return: Return the sum of all emissions (in mg) from the simulation of reference | ||||||
|  |         """ | ||||||
|         if self.n_steps == 200: |         if self.n_steps == 200: | ||||||
|             return self.ref200 |             return self.ref200 | ||||||
|   | |||||||
| @@ -7,12 +7,12 @@ | |||||||
| 	"n_steps": 200, | 	"n_steps": 200, | ||||||
| 	"window_size":100, | 	"window_size":100, | ||||||
| 	 | 	 | ||||||
| 	"without_actions_mode": true, | 	"without_actions_mode": false, | ||||||
| 	 | 	 | ||||||
| 	"limit_speed_mode": false, | 	"limit_speed_mode": true, | ||||||
| 	"speed_rf": 0.1, | 	"speed_rf": 0.1, | ||||||
| 	 | 	 | ||||||
| 	"adjust_traffic_light_mode": false, | 	"adjust_traffic_light_mode": true, | ||||||
| 	"trafficLights_duration_rf": 0.2, | 	"trafficLights_duration_rf": 0.2, | ||||||
| 	 | 	 | ||||||
| 	"weight_routing_mode": false, | 	"weight_routing_mode": false, | ||||||
|   | |||||||
| @@ -1,3 +1,9 @@ | |||||||
|  | """ | ||||||
|  | Created on 17 oct. 2018 | ||||||
|  |  | ||||||
|  | @author: Axel Huynh-Phuc, Thibaud Gasser | ||||||
|  | """ | ||||||
|  |  | ||||||
| import argparse | import argparse | ||||||
| import csv | import csv | ||||||
| import datetime | import datetime | ||||||
| @@ -7,15 +13,25 @@ import sys | |||||||
| import time | import time | ||||||
| from typing import List | from typing import List | ||||||
|  |  | ||||||
|  | import actions | ||||||
| import traci | import traci | ||||||
|  | from config import Config | ||||||
|  | from model import Area, Vehicle, Lane, TrafficLight, Phase, Logic, Emission | ||||||
| from parse import search | from parse import search | ||||||
| from shapely.geometry import LineString | from shapely.geometry import LineString | ||||||
|  |  | ||||||
| import actions | """ | ||||||
| from config import Config | This module defines the entry point of the application  | ||||||
| from model import Area, Vehicle, Lane, TrafficLight, Phase, Logic, Emission | """ | ||||||
|  |  | ||||||
| def init_grid(simulation_bounds, areas_number, window_size): | def init_grid(simulation_bounds, areas_number, window_size): | ||||||
|  |     """ | ||||||
|  |     Initialize the grid of the loaded map from the configuration | ||||||
|  |     :param simulation_bounds: The map bounds | ||||||
|  |     :param areas_number: The number of areas | ||||||
|  |     :param window_size: The size of the acquisition window | ||||||
|  |     :return: A list of areas | ||||||
|  |     """ | ||||||
|     grid = list() |     grid = list() | ||||||
|     width = simulation_bounds[1][0] / areas_number |     width = simulation_bounds[1][0] / areas_number | ||||||
|     height = simulation_bounds[1][1] / areas_number |     height = simulation_bounds[1][1] / areas_number | ||||||
| @@ -32,6 +48,10 @@ def init_grid(simulation_bounds, areas_number, window_size): | |||||||
|  |  | ||||||
|  |  | ||||||
| def get_all_lanes() -> List[Lane]: | def get_all_lanes() -> List[Lane]: | ||||||
|  |     """ | ||||||
|  |     Recover and creates a list of Lane objects | ||||||
|  |     :return: The lanes list | ||||||
|  |     """ | ||||||
|     lanes = [] |     lanes = [] | ||||||
|     for lane_id in traci.lane.getIDList(): |     for lane_id in traci.lane.getIDList(): | ||||||
|         polygon_lane = LineString(traci.lane.getShape(lane_id)) |         polygon_lane = LineString(traci.lane.getShape(lane_id)) | ||||||
| @@ -41,6 +61,12 @@ def get_all_lanes() -> List[Lane]: | |||||||
|  |  | ||||||
|  |  | ||||||
| def parse_phase(phase_repr): | def parse_phase(phase_repr): | ||||||
|  |     """ | ||||||
|  |     Because the SUMO object Phase does not contain accessors, | ||||||
|  |     we parse the string representation to retrieve data members. | ||||||
|  |     :param phase_repr: The Phase string representation | ||||||
|  |     :return: An new Phase instance | ||||||
|  |     """ | ||||||
|     duration = search('duration: {:f}', phase_repr) |     duration = search('duration: {:f}', phase_repr) | ||||||
|     min_duration = search('minDuration: {:f}', phase_repr) |     min_duration = search('minDuration: {:f}', phase_repr) | ||||||
|     max_duration = search('maxDuration: {:f}', phase_repr) |     max_duration = search('maxDuration: {:f}', phase_repr) | ||||||
| @@ -55,6 +81,11 @@ def parse_phase(phase_repr): | |||||||
|  |  | ||||||
|  |  | ||||||
| def add_data_to_areas(areas: List[Area]): | def add_data_to_areas(areas: List[Area]): | ||||||
|  |     """ | ||||||
|  |     Adds all recovered data to different areas | ||||||
|  |     :param areas: The list of areas | ||||||
|  |     :return: | ||||||
|  |     """ | ||||||
|     lanes = get_all_lanes() |     lanes = get_all_lanes() | ||||||
|     for area in areas: |     for area in areas: | ||||||
|         for lane in lanes:  # add lanes  |         for lane in lanes:  # add lanes  | ||||||
| @@ -72,6 +103,11 @@ def add_data_to_areas(areas: List[Area]): | |||||||
|  |  | ||||||
|  |  | ||||||
| def compute_vehicle_emissions(veh_id): | def compute_vehicle_emissions(veh_id): | ||||||
|  |     """ | ||||||
|  |     Recover the emissions of different pollutants from a vehicle and create an Emission instance | ||||||
|  |     :param veh_id: | ||||||
|  |     :return: A new Emission instance | ||||||
|  |     """ | ||||||
|     co2 = traci.vehicle.getCO2Emission(veh_id) |     co2 = traci.vehicle.getCO2Emission(veh_id) | ||||||
|     co = traci.vehicle.getCOEmission(veh_id) |     co = traci.vehicle.getCOEmission(veh_id) | ||||||
|     nox = traci.vehicle.getNOxEmission(veh_id) |     nox = traci.vehicle.getNOxEmission(veh_id) | ||||||
| @@ -82,6 +118,10 @@ def compute_vehicle_emissions(veh_id): | |||||||
|  |  | ||||||
|  |  | ||||||
| def get_all_vehicles() -> List[Vehicle]: | def get_all_vehicles() -> List[Vehicle]: | ||||||
|  |     """ | ||||||
|  |     Recover all useful information about vehicles and creates a vehicles list | ||||||
|  |     :return: A list of vehicles instances | ||||||
|  |     """ | ||||||
|     vehicles = list() |     vehicles = list() | ||||||
|     for veh_id in traci.vehicle.getIDList(): |     for veh_id in traci.vehicle.getIDList(): | ||||||
|         veh_pos = traci.vehicle.getPosition(veh_id) |         veh_pos = traci.vehicle.getPosition(veh_id) | ||||||
| @@ -92,6 +132,16 @@ def get_all_vehicles() -> List[Vehicle]: | |||||||
|  |  | ||||||
|  |  | ||||||
| def get_emissions(grid: List[Area], vehicles: List[Vehicle], current_step, config, logger): | def get_emissions(grid: List[Area], vehicles: List[Vehicle], current_step, config, logger): | ||||||
|  |     """ | ||||||
|  |     For each area retrieves the acquired emissions in the window, | ||||||
|  |     and acts according to the configuration chosen by the user | ||||||
|  |     :param grid: The list of areas | ||||||
|  |     :param vehicles: The list of vehicles | ||||||
|  |     :param current_step: The simulation current step | ||||||
|  |     :param config: The simulation configuration | ||||||
|  |     :param logger: The simulation logger | ||||||
|  |     :return: | ||||||
|  |     """ | ||||||
|     for area in grid: |     for area in grid: | ||||||
|         total_emissions = Emission() |         total_emissions = Emission() | ||||||
|         for vehicle in vehicles: |         for vehicle in vehicles: | ||||||
| @@ -100,11 +150,11 @@ def get_emissions(grid: List[Area], vehicles: List[Vehicle], current_step, confi | |||||||
|  |  | ||||||
|         area.emissions_by_step.append(total_emissions) |         area.emissions_by_step.append(total_emissions) | ||||||
|  |  | ||||||
|         if area.sum_emissions_into_window(current_step, config.window_size) >= config.emissions_threshold: |         if area.sum_emissions_into_window(current_step) >= config.emissions_threshold: | ||||||
|  |  | ||||||
|             if config.limit_speed_mode and not area.limited_speed: |             if config.limit_speed_mode and not area.limited_speed: | ||||||
|                 logger.info(f'Action - Decreased max speed into {area.name} by {config.speed_rf * 100}%') |                 logger.info(f'Action - Decreased max speed into {area.name} by {config.speed_rf * 100}%') | ||||||
|                 actions.limit_speed_into_area(area, vehicles, config.speed_rf) |                 actions.limit_speed_into_area(area, config.speed_rf) | ||||||
|                 if config.adjust_traffic_light_mode and not area.tls_adjusted: |                 if config.adjust_traffic_light_mode and not area.tls_adjusted: | ||||||
|                     logger.info( |                     logger.info( | ||||||
|                         f'Action - Decreased traffic lights duration by {config.trafficLights_duration_rf * 100}%') |                         f'Action - Decreased traffic lights duration by {config.trafficLights_duration_rf * 100}%') | ||||||
| @@ -126,11 +176,23 @@ def get_emissions(grid: List[Area], vehicles: List[Vehicle], current_step, confi | |||||||
|  |  | ||||||
|  |  | ||||||
| def get_reduction_percentage(ref, total): | def get_reduction_percentage(ref, total): | ||||||
|  |     """ | ||||||
|  |     Return the reduction percentage of total emissions between reference and an other simulation | ||||||
|  |     :param ref: | ||||||
|  |     :param total: | ||||||
|  |     :return: | ||||||
|  |     """ | ||||||
|     return (ref - total) / ref * 100 |     return (ref - total) / ref * 100 | ||||||
|  |  | ||||||
|  |  | ||||||
| def export_data_to_csv(config, grid): | def export_data_to_csv(config, grid): | ||||||
|     csv_dir = os.path.join(SCRIPTDIR, 'csv') |     """ | ||||||
|  |     Export all Emission objects as a CSV file into the csv directory | ||||||
|  |     :param config: The simulation configuration | ||||||
|  |     :param grid: The list of areas | ||||||
|  |     :return: | ||||||
|  |     """ | ||||||
|  |     csv_dir = 'csv' | ||||||
|     if not os.path.exists(csv_dir): |     if not os.path.exists(csv_dir): | ||||||
|         os.mkdir(csv_dir) |         os.mkdir(csv_dir) | ||||||
|  |  | ||||||
| @@ -147,6 +209,12 @@ def export_data_to_csv(config, grid): | |||||||
|  |  | ||||||
|  |  | ||||||
| def run(config, logger): | def run(config, logger): | ||||||
|  |     """ | ||||||
|  |     Run the simulation with the configuration chosen | ||||||
|  |     :param config: The simulation configuration | ||||||
|  |     :param logger: The simulation logger | ||||||
|  |     :return: | ||||||
|  |     """ | ||||||
|     grid = list() |     grid = list() | ||||||
|     try: |     try: | ||||||
|         traci.start(config.sumo_cmd) |         traci.start(config.sumo_cmd) | ||||||
| @@ -199,6 +267,11 @@ def run(config, logger): | |||||||
|  |  | ||||||
|  |  | ||||||
| def add_options(parser): | def add_options(parser): | ||||||
|  |     """ | ||||||
|  |     Add command line options | ||||||
|  |     :param parser: The command line parser | ||||||
|  |     :return: | ||||||
|  |     """ | ||||||
|     parser.add_argument("-f", "--configfile", type=str, default='configs/default_config.json', required=False, |     parser.add_argument("-f", "--configfile", type=str, default='configs/default_config.json', required=False, | ||||||
|                         help='Choose your configuration file from your working directory') |                         help='Choose your configuration file from your working directory') | ||||||
|     parser.add_argument("-save", "--save", action="store_true", |     parser.add_argument("-save", "--save", action="store_true", | ||||||
| @@ -212,6 +285,11 @@ def add_options(parser): | |||||||
|  |  | ||||||
|  |  | ||||||
| def main(args): | def main(args): | ||||||
|  |     """ | ||||||
|  |     The entry point of the application | ||||||
|  |     :param args: Command line options | ||||||
|  |     :return: | ||||||
|  |     """ | ||||||
|     parser = argparse.ArgumentParser(description="") |     parser = argparse.ArgumentParser(description="") | ||||||
|     add_options(parser) |     add_options(parser) | ||||||
|     args = parser.parse_args(args) |     args = parser.parse_args(args) | ||||||
|   | |||||||
| @@ -1,3 +1,9 @@ | |||||||
|  | """ | ||||||
|  | Created on 17 oct. 2018 | ||||||
|  |  | ||||||
|  | @author: Axel Huynh-Phuc, Thibaud Gasser | ||||||
|  | """ | ||||||
|  |  | ||||||
| import collections | import collections | ||||||
| from typing import Tuple, Set | from typing import Tuple, Set | ||||||
|  |  | ||||||
| @@ -6,10 +12,25 @@ from shapely.geometry import Polygon | |||||||
| from shapely.geometry.base import BaseGeometry | from shapely.geometry.base import BaseGeometry | ||||||
| from traci._trafficlight import Logic as SUMO_Logic | from traci._trafficlight import Logic as SUMO_Logic | ||||||
|  |  | ||||||
|  | """ | ||||||
|  | This module defines the business model of our application | ||||||
|  | """ | ||||||
|  |  | ||||||
|  |  | ||||||
| class Lane: | class Lane: | ||||||
|  |     """ | ||||||
|  |     The Lane class includes the polygon defining the lane | ||||||
|  |     and keep in memory the initial maximum speed on the lane | ||||||
|  |     """ | ||||||
|  |  | ||||||
|     def __init__(self, lane_id: str, polygon: LineString, initial_max_speed: float): |     def __init__(self, lane_id: str, polygon: LineString, initial_max_speed: float): | ||||||
|  |         """ | ||||||
|  |         Lane constructor | ||||||
|  |  | ||||||
|  |         :param lane_id: The ID of the lane | ||||||
|  |         :param polygon: The polygon defining the shape of the lane | ||||||
|  |         :param initial_max_speed: The initial maximum speed | ||||||
|  |         """ | ||||||
|         self.polygon = polygon |         self.polygon = polygon | ||||||
|         self.lane_id = lane_id |         self.lane_id = lane_id | ||||||
|         self.initial_max_speed = initial_max_speed |         self.initial_max_speed = initial_max_speed | ||||||
| @@ -20,26 +41,62 @@ class Lane: | |||||||
|  |  | ||||||
|  |  | ||||||
| class Phase: | class Phase: | ||||||
|  |     """ | ||||||
|  |     The Phase class defines a phase of a traffic light | ||||||
|  |     """ | ||||||
|  |  | ||||||
|     def __init__(self, duration: float, minDuration: float, maxDuration: float, phaseDef: str): |     def __init__(self, duration: float, minDuration: float, maxDuration: float, phaseDef: str): | ||||||
|  |         """ | ||||||
|  |         Phase constructor | ||||||
|  |  | ||||||
|  |         :param duration: The duration of the phase (in seconds) | ||||||
|  |         :param minDuration: The minimum duration of the phase | ||||||
|  |         :param maxDuration: The maximum duration of the phase | ||||||
|  |         :param phaseDef: The definition of the phase, following the definition rules of SUMO | ||||||
|  |         (See : http://sumo.dlr.de/wiki/Simulation/Traffic_Lights#.3Cphase.3E_Attributes) | ||||||
|  |         """ | ||||||
|  |  | ||||||
|         self.duration = duration |         self.duration = duration | ||||||
|         self.minDuration = minDuration |         self.minDuration = minDuration | ||||||
|         self.maxDuration = maxDuration |         self.maxDuration = maxDuration | ||||||
|         self.phaseDef = phaseDef |         self.phaseDef = phaseDef | ||||||
|  |  | ||||||
|     def __repr__(self) -> str: |     def __repr__(self) -> str: | ||||||
|  |         """ | ||||||
|  |         :return: The Phase string representation | ||||||
|  |         """ | ||||||
|         repr = f'Phase(duration:{self.duration},minDuration:{self.minDuration},maxDuration:{self.maxDuration},phaseDef:{self.phaseDef})' |         repr = f'Phase(duration:{self.duration},minDuration:{self.minDuration},maxDuration:{self.maxDuration},phaseDef:{self.phaseDef})' | ||||||
|         return str(repr) |         return str(repr) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Logic: | class Logic: | ||||||
|  |     """ | ||||||
|  |     The Logic class defines the strategy of a traffic light. | ||||||
|  |     This class includes the Logic instance of SUMO with all phases corresponding to it. | ||||||
|  |     A Logic object contains multiple phases. | ||||||
|  |     """ | ||||||
|  |  | ||||||
|     def __init__(self, logic: SUMO_Logic, phases: Set[Phase]): |     def __init__(self, logic: SUMO_Logic, phases: Set[Phase]): | ||||||
|  |         """ | ||||||
|  |         Logic constructor | ||||||
|  |         :param logic: The SUMO Logic object | ||||||
|  |         :param phases: The list of phases belonging to this logic | ||||||
|  |         """ | ||||||
|         self._logic = logic |         self._logic = logic | ||||||
|         self._phases: Set[Phase] = phases |         self._phases: Set[Phase] = phases | ||||||
|  |  | ||||||
|  |  | ||||||
| class TrafficLight: | class TrafficLight: | ||||||
|  |     """ | ||||||
|  |     This TrafficLight class defines a traffic light | ||||||
|  |     """ | ||||||
|  |  | ||||||
|     def __init__(self, tl_id: str, logics: Set[Logic]): |     def __init__(self, tl_id: str, logics: Set[Logic]): | ||||||
|  |         """ | ||||||
|  |         TrafficLight constructor | ||||||
|  |         :param tl_id: The traffic light ID | ||||||
|  |         :param logics: The list of logics belonging to the traffic light | ||||||
|  |         """ | ||||||
|         self.tl_id = tl_id |         self.tl_id = tl_id | ||||||
|         self._logics: Set[Logic] = logics |         self._logics: Set[Logic] = logics | ||||||
|  |  | ||||||
| @@ -49,7 +106,19 @@ class TrafficLight: | |||||||
|  |  | ||||||
|  |  | ||||||
| class Emission: | class Emission: | ||||||
|  |     """ | ||||||
|  |     This class defines the different pollutant emissions | ||||||
|  |     """ | ||||||
|  |  | ||||||
|     def __init__(self, co2=0, co=0, nox=0, hc=0, pmx=0): |     def __init__(self, co2=0, co=0, nox=0, hc=0, pmx=0): | ||||||
|  |         """ | ||||||
|  |         Emission constructor | ||||||
|  |         :param co2: Quantity of CO2(in mg) | ||||||
|  |         :param co: Quantity of C0(in mg) | ||||||
|  |         :param nox: Quantity of Nox(in mg) | ||||||
|  |         :param hc: Quantity of HC(in mg) | ||||||
|  |         :param pmx: Quantity of PMx(in mg) | ||||||
|  |         """ | ||||||
|         self.co2 = co2 |         self.co2 = co2 | ||||||
|         self.co = co |         self.co = co | ||||||
|         self.nox = nox |         self.nox = nox | ||||||
| @@ -57,20 +126,41 @@ class Emission: | |||||||
|         self.pmx = pmx |         self.pmx = pmx | ||||||
|  |  | ||||||
|     def __add__(self, other): |     def __add__(self, other): | ||||||
|  |         """ | ||||||
|  |         Add two emission objects | ||||||
|  |         :param other: The other Emission object to add | ||||||
|  |         :return: A new object whose emission values are the sum of both Emission object | ||||||
|  |         """ | ||||||
|         return Emission(self.co2 + other.co2, self.co + other.co, self.nox + other.nox, self.hc + other.hc, |         return Emission(self.co2 + other.co2, self.co + other.co, self.nox + other.nox, self.hc + other.hc, | ||||||
|                         self.pmx + other.pmx) |                         self.pmx + other.pmx) | ||||||
|  |  | ||||||
|     def value(self): |     def value(self): | ||||||
|  |         """ | ||||||
|  |         :return: The sum of all emissions | ||||||
|  |         """ | ||||||
|         return self.co2 + self.co + self.nox + self.hc + self.pmx |         return self.co2 + self.co + self.nox + self.hc + self.pmx | ||||||
|  |  | ||||||
|     def __repr__(self) -> str: |     def __repr__(self) -> str: | ||||||
|  |         """ | ||||||
|  |         :return: The Emission string representation | ||||||
|  |         """ | ||||||
|         repr = f'Emission(co2={self.co2},co={self.co},nox={self.nox},hc={self.hc},pmx={self.pmx})' |         repr = f'Emission(co2={self.co2},co={self.co},nox={self.nox},hc={self.hc},pmx={self.pmx})' | ||||||
|         return str(repr) |         return str(repr) | ||||||
|  |  | ||||||
|  |  | ||||||
| class Area: | class Area: | ||||||
|  |     """ | ||||||
|  |     The Area class defines a grid area of the map | ||||||
|  |     """ | ||||||
|  |  | ||||||
|     def __init__(self, coords, name, window_size): |     def __init__(self, coords, name, window_size): | ||||||
|  |         """ | ||||||
|  |         Area constructor | ||||||
|  |         :param coords: The coordinates of the zone, | ||||||
|  |         defined by the bounds coordinates of this area : (xmin, ymin, xmax, ymax) | ||||||
|  |         :param name: The Area name | ||||||
|  |         :param window_size: The size of the acquisition window | ||||||
|  |         """ | ||||||
|         self.limited_speed = False |         self.limited_speed = False | ||||||
|         self.locked = False |         self.locked = False | ||||||
|         self.tls_adjusted = False |         self.tls_adjusted = False | ||||||
| @@ -83,35 +173,75 @@ class Area: | |||||||
|         self._tls: Set[TrafficLight] = set() |         self._tls: Set[TrafficLight] = set() | ||||||
|  |  | ||||||
|     def __eq__(self, other): |     def __eq__(self, other): | ||||||
|  |         """ | ||||||
|  |         Overrides the equal definition | ||||||
|  |         :param other: The other Area object | ||||||
|  |         :return: True if the two rectangles are equals | ||||||
|  |         """ | ||||||
|         return self.rectangle.__eq__(other) |         return self.rectangle.__eq__(other) | ||||||
|  |  | ||||||
|     def __contains__(self, item): |     def __contains__(self, item): | ||||||
|  |         """ | ||||||
|  |         :param item: A position on the map | ||||||
|  |         :return: True if the area contains the item | ||||||
|  |         """ | ||||||
|         return self.rectangle.contains(item) |         return self.rectangle.contains(item) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def bounds(self): |     def bounds(self): | ||||||
|  |         """ | ||||||
|  |         Return the bounds rectangle of this area | ||||||
|  |         :return: | ||||||
|  |         """ | ||||||
|         return self.rectangle.bounds |         return self.rectangle.bounds | ||||||
|  |  | ||||||
|     def intersects(self, other: BaseGeometry) -> bool: |     def intersects(self, other: BaseGeometry) -> bool: | ||||||
|  |         """ | ||||||
|  |         :param other: A BaseGeometry object | ||||||
|  |         :return: True if this area intersects with other | ||||||
|  |         """ | ||||||
|         return self.rectangle.intersects(other) |         return self.rectangle.intersects(other) | ||||||
|  |  | ||||||
|     def add_lane(self, lane: Lane): |     def add_lane(self, lane: Lane): | ||||||
|  |         """ | ||||||
|  |         Add a new lane object into lanes list | ||||||
|  |         :param lane: A Lane object | ||||||
|  |         :return: | ||||||
|  |         """ | ||||||
|         self._lanes.add(lane) |         self._lanes.add(lane) | ||||||
|  |  | ||||||
|     def add_tl(self, tl: TrafficLight): |     def add_tl(self, tl: TrafficLight): | ||||||
|  |         """ | ||||||
|  |         Add a new trafficLight object into lanes list | ||||||
|  |         :param tl: A TrafficLight object | ||||||
|  |         :return: | ||||||
|  |         """ | ||||||
|         self._tls.add(tl) |         self._tls.add(tl) | ||||||
|  |  | ||||||
|     def remove_lane(self, lane: Lane): |     def remove_lane(self, lane: Lane): | ||||||
|  |         """ | ||||||
|  |         Remove a lane from lanes list | ||||||
|  |         :param lane: The Lane object to remove | ||||||
|  |         :return: | ||||||
|  |         """ | ||||||
|         self._lanes.remove(lane) |         self._lanes.remove(lane) | ||||||
|  |  | ||||||
|     def sum_all_emissions(self): |     def sum_all_emissions(self): | ||||||
|  |         """ | ||||||
|  |         Sum all Emissions object from initial step to final step | ||||||
|  |         :return: The sum Emission object | ||||||
|  |         """ | ||||||
|         sum = Emission() |         sum = Emission() | ||||||
|         for emission in self.emissions_by_step: |         for emission in self.emissions_by_step: | ||||||
|             sum += emission |             sum += emission | ||||||
|         return sum |         return sum | ||||||
|  |  | ||||||
|     def sum_emissions_into_window(self, current_step, window_size): |     def sum_emissions_into_window(self, current_step): | ||||||
|  |         """ | ||||||
|  |         Sum all Emissions object into the acquisition window | ||||||
|  |         :param current_step: The current step of the simulation | ||||||
|  |         :return: | ||||||
|  |         """ | ||||||
|         self.window.appendleft(self.emissions_by_step[current_step].value()) |         self.window.appendleft(self.emissions_by_step[current_step].value()) | ||||||
|  |  | ||||||
|         sum = 0 |         sum = 0 | ||||||
| @@ -129,11 +259,22 @@ class Area: | |||||||
|  |  | ||||||
|  |  | ||||||
| class Vehicle: | class Vehicle: | ||||||
|  |     """ | ||||||
|  |     The Vehicle class defines a vehicle object | ||||||
|  |     """ | ||||||
|  |  | ||||||
|     def __init__(self, veh_id: int, pos: Tuple[float, float]): |     def __init__(self, veh_id: int, pos: Tuple[float, float]): | ||||||
|  |         """ | ||||||
|  |         Vehicle constructor | ||||||
|  |         :param veh_id: The vehicle ID | ||||||
|  |         :param pos: The position of the vehicle one the map | ||||||
|  |         """ | ||||||
|         self.emissions: Emission = Emission() |         self.emissions: Emission = Emission() | ||||||
|         self.veh_id = veh_id |         self.veh_id = veh_id | ||||||
|         self.pos = Point(pos) |         self.pos = Point(pos) | ||||||
|  |  | ||||||
|     def __repr__(self) -> str: |     def __repr__(self) -> str: | ||||||
|  |         """ | ||||||
|  |         :return: The Vehicle string representation | ||||||
|  |         """ | ||||||
|         return str(self.__dict__) |         return str(self.__dict__) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user