class Router(object): def __init__(self): self.mongodb_database_connection = MongodbDatabaseConnection(host=mongodb_host, port=mongodb_port) log(module_name='Router', log_type='DEBUG', log_message='mongodb_database_connection: established') def get_bus_stop(self, name=None, provided_point=None, longitude=None, latitude=None): """ Get a bus_stop_document. :param name: string :param provided_point: Point :param longitude: float :param latitude: float :return: bus_stop: bus_stop_document """ bus_stop = None if name is not None: bus_stop = self.mongodb_database_connection.find_bus_stop_document(name=name) elif provided_point is not None: bus_stop_documents = self.mongodb_database_connection.find_bus_stop_documents() bus_stop = self.get_bus_stop_closest_to_point( bus_stop_documents=bus_stop_documents, provided_point=provided_point ) elif longitude is not None and latitude is not None: point = Point(longitude=longitude, latitude=latitude) bus_stop_documents = self.mongodb_database_connection.find_bus_stop_documents() bus_stop = self.get_bus_stop_closest_to_point( bus_stop_documents=bus_stop_documents, provided_point=point ) else: pass return bus_stop @staticmethod def get_bus_stop_closest_to_point(bus_stop_documents, provided_point): """ Get the bus stop which is closest to a geographic point. :param bus_stop_documents: [bus_stop_document] :param provided_point: Point :return closest_bus_stop: bus_stop_document """ minimum_distance = float('Inf') closest_bus_stop = None for bus_stop_document in bus_stop_documents: bus_stop_document_point = bus_stop_document.get('point') current_distance = distance( point_one=provided_point, longitude_two=bus_stop_document_point.get('longitude'), latitude_two=bus_stop_document_point.get('latitude') ) if current_distance == 0: closest_bus_stop = bus_stop_document break elif current_distance < minimum_distance: minimum_distance = current_distance closest_bus_stop = bus_stop_document else: pass return closest_bus_stop def get_bus_stops(self, names): """ Get multiple bus_stop_documents. :param names: [string] :return: bus_stops: [bus_stop_document] """ bus_stops = [] # for name in names: # bus_stop = self.get_bus_stop(name=name) # bus_stops.append(bus_stop) bus_stop_documents = self.mongodb_database_connection.find_bus_stop_documents(names=names) bus_stop_documents_dictionary = {} for bus_stop_document in bus_stop_documents: bus_stop_document_name = bus_stop_document.get('name') bus_stop_documents_dictionary[bus_stop_document_name] = bus_stop_document for name in names: bus_stop_document = bus_stop_documents_dictionary.get(name) bus_stops.append(bus_stop_document) return bus_stops def get_bus_stops_dictionary(self): """ Retrieve a dictionary containing all the documents of the BusStops collection. :return: bus_stops_dictionary: {name -> {'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}} """ bus_stops_dictionary = self.mongodb_database_connection.find_bus_stop_documents(in_dictionary=True) return bus_stops_dictionary def get_bus_stops_list(self): """ Retrieve a list containing all the documents of the BusStops collection. :return: bus_stops_list: [{'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}] """ bus_stops_list = self.mongodb_database_connection.find_bus_stop_documents() return bus_stops_list def get_edges_dictionary(self): """ Retrieve a dictionary containing all the documents of the Edges collection. :return: {starting_node_osm_id -> [{'_id', 'starting_node': {'osm_id', 'point': {'longitude', 'latitude'}}, 'ending_node': {'osm_id', 'point': {'longitude', 'latitude'}}, 'max_speed', 'road_type', 'way_id', 'traffic_density'}]} """ edges_dictionary = self.mongodb_database_connection.find_edge_documents(in_dictionary=True) return edges_dictionary def get_edges_list(self): """ Retrieve a list containing all the documents of the Edges collection. :return: edges_list: [{'_id', 'starting_node': {'osm_id', 'point': {'longitude', 'latitude'}}, 'ending_node': {'osm_id', 'point': {'longitude', 'latitude'}}, 'max_speed', 'road_type', 'way_id', 'traffic_density'}] """ edges_list = self.mongodb_database_connection.find_edge_documents() return edges_list def get_points_dictionary(self): """ Retrieve a dictionary containing all the documents of the Points collection. :return points_dictionary: {osm_id -> {'_id', 'osm_id', 'point': {'longitude', 'latitude'}}} """ points_dictionary = self.mongodb_database_connection.find_point_documents(in_dictionary=True) return points_dictionary def get_route_between_two_bus_stops(self, starting_bus_stop=None, ending_bus_stop=None, starting_bus_stop_name=None, ending_bus_stop_name=None, edges_dictionary=None): """ Identify the less time-consuming route between two bus_stops. :param starting_bus_stop: bus_stop_document :param ending_bus_stop: bus_stop_document :param starting_bus_stop_name: string :param ending_bus_stop_name: string :param edges_dictionary: {starting_node_osm_id -> [edge_document]} :return response: get_route_between_two_bus_stops """ if starting_bus_stop is None and starting_bus_stop_name is not None: starting_bus_stop = self.get_bus_stop(name=starting_bus_stop_name) if ending_bus_stop is None and ending_bus_stop_name is not None: ending_bus_stop = self.get_bus_stop(name=ending_bus_stop_name) if edges_dictionary is None: edges_dictionary = self.get_edges_dictionary() route = identify_path_with_lowest_cost( start=starting_bus_stop, end=ending_bus_stop, edges_dictionary=edges_dictionary ) response = { 'starting_bus_stop': starting_bus_stop, 'ending_bus_stop': ending_bus_stop, 'route': route } return response def get_route_between_multiple_bus_stops(self, bus_stops=None, bus_stop_names=None): """ Identify the less time-consuming route between multiple bus_stops. :param bus_stops: [bus_stop_document] :param bus_stop_names: string :return response: get_route_between_multiple_bus_stops """ response = [] edges_dictionary = self.get_edges_dictionary() if bus_stops is None and bus_stop_names is not None: bus_stops = self.get_bus_stops(names=bus_stop_names) for i in range(0, len(bus_stops) - 1): starting_bus_stop = bus_stops[i] ending_bus_stop = bus_stops[i + 1] intermediate_route = self.get_route_between_two_bus_stops( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop, edges_dictionary=edges_dictionary ) response.append(intermediate_route) return response def get_waypoints_between_two_bus_stops(self, starting_bus_stop=None, ending_bus_stop=None, starting_bus_stop_name=None, ending_bus_stop_name=None): """ Identify all possible route connections between two bus_stops. :param starting_bus_stop: bus_stop_document :param ending_bus_stop: bus_stop_document :param starting_bus_stop_name: string :param ending_bus_stop_name: string :return response: get_waypoints_between_two_bus_stops """ if starting_bus_stop is None and starting_bus_stop_name is not None: starting_bus_stop = self.get_bus_stop(name=starting_bus_stop_name) if ending_bus_stop is None and ending_bus_stop_name is not None: ending_bus_stop = self.get_bus_stop(name=ending_bus_stop_name) edges_dictionary = self.get_edges_dictionary() waypoints = identify_all_paths( starting_node_osm_id=starting_bus_stop.get('osm_id'), ending_node_osm_id=ending_bus_stop.get('osm_id'), edges_dictionary=edges_dictionary ) response = { 'starting_bus_stop': starting_bus_stop, 'ending_bus_stop': ending_bus_stop, 'waypoints': waypoints } return response def get_waypoints_between_multiple_bus_stops(self, bus_stops=None, bus_stop_names=None): """ Identify all possible route connections between multiple bus_stops. :param bus_stops: [bus_stop_document] :param bus_stop_names: string :return response: get_waypoints_between_multiple_bus_stops """ response = [] if bus_stops is None and bus_stop_names is not None: bus_stops = self.get_bus_stops(names=bus_stop_names) edges_dictionary = self.get_edges_dictionary() for i in range(0, len(bus_stops) - 1): starting_bus_stop = bus_stops[i] ending_bus_stop = bus_stops[i + 1] waypoints = identify_all_paths( starting_node_osm_id=starting_bus_stop.get('osm_id'), ending_node_osm_id=ending_bus_stop.get('osm_id'), edges_dictionary=edges_dictionary ) intermediate_response = { 'starting_bus_stop': starting_bus_stop, 'ending_bus_stop': ending_bus_stop, 'waypoints': waypoints } response.append(intermediate_response) return response
class LookAheadHandler(object): def __init__(self): self.mongodb_database_connection = MongodbDatabaseConnection(host=mongodb_host, port=mongodb_port) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='mongodb_database_connection: established') def generate_bus_line(self, bus_stop_names, bus_line_id=None): """ Generate a bus_line, consisted of a bus_line_id and a list of bus_stops, and store it to the corresponding collection of the System Database. Moreover, identify all the possible waypoints between the bus_stops of the bus_line, and populate the BusStopWaypoints collection. :param bus_stop_names: [string] :param bus_line_id: int :return: None """ # 1: The inputs: bus_line_id and bus_stop_names are provided to the function, so as as a bus_line # with the corresponding bus_line_id and bus_stops to be generated. # # 2: The Look Ahead connects to the System Database and retrieves the bus_stops which correspond to # the provided bus_stop_names. The function returns None and the bus_line is not generated, # in case there is a bus_stop_name which does not correspond to a stored bus_stop. # if bus_line_id is None: maximum_bus_line_id = self.mongodb_database_connection.get_maximum_or_minimum(collection='bus_line') bus_line_id = maximum_bus_line_id + 1 bus_stops = [] for bus_stop_name in bus_stop_names: bus_stop_document = self.mongodb_database_connection.find_bus_stop_document(name=bus_stop_name) if bus_stop_document is None: log_message = 'find_bus_stop_document (mongodb_database) - name:', bus_stop_name, '- result: None' log(module_name='look_ahead_handler', log_type='DEBUG', log_message=log_message) return None else: bus_stops.append(bus_stop_document) # 3: The intermediate waypoints of the bus_routes, which are generated while combining starting and # ending bus_stops of the bus_line, should be stored as bus_stop_waypoints_documents at the System Database. # The Look Ahead checks the existing bus_stop_waypoints_documents and communicates with the Route Generator # in order to identify the waypoints of the bus_routes which are not already stored. The newly generated # bus_stop_waypoints_documents are getting stored to the corresponding collection of the System Database. # The function returns None and the bus_line is not generated, in case the Route Generator can not identify # a possible route in order to connect the bus_stops of the bus_line. # number_of_bus_stops = len(bus_stops) for i in range(0, number_of_bus_stops - 1): starting_bus_stop = bus_stops[i] ending_bus_stop = bus_stops[i + 1] bus_stop_waypoints_document = self.mongodb_database_connection.find_bus_stop_waypoints_document( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop ) if bus_stop_waypoints_document is None: route_generator_response = get_waypoints_between_two_bus_stops( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop ) if route_generator_response is None: log(module_name='look_ahead_handler', log_type='DEBUG', log_message='get_waypoints_between_two_bus_stops (route_generator): None') return None else: waypoints = route_generator_response.get('waypoints') if len(waypoints) == 0: log(module_name='look_ahead_handler', log_type='DEBUG', log_message='get_waypoints_between_two_bus_stops (route_generator): None') return None lists_of_edge_object_ids = [] for list_of_edges in waypoints: list_of_edge_object_ids = [] for edge in list_of_edges: edge_object_id = edge.get('_id') list_of_edge_object_ids.append(edge_object_id) lists_of_edge_object_ids.append(list_of_edge_object_ids) # waypoints: [[edge_object_id]] # waypoints = lists_of_edge_object_ids self.mongodb_database_connection.insert_bus_stop_waypoints_document( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop, waypoints=waypoints ) # 4: The Look Ahead stores the newly generated bus_line_document, which is consisted of the bus_line_id # and the list of bus_stops, to the corresponding collection of the System Database. # In case there is an already existing bus_line_document, with the same bus_line_id, # then the list of bus_stops gets updated. # bus_line_document = {'bus_line_id': bus_line_id, 'bus_stops': bus_stops} self.mongodb_database_connection.insert_bus_line_document(bus_line_document=bus_line_document) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='insert_bus_line_document (mongodb_database): ok') def generate_timetables_for_bus_line(self, timetables_starting_datetime, timetables_ending_datetime, requests_min_departure_datetime, requests_max_departure_datetime, bus_line=None, bus_line_id=None): """ Generate timetables for a bus_line, for a selected datetime period, evaluating travel_requests of a specific datetime period. - The input: timetables_starting_datetime and input: timetables_ending_datetime are provided to the function, so as timetables for the specific datetime period to be generated. - The input: requests_min_departure_datetime and input: requests_max_departure_datetime are provided to the function, so as travel_requests with departure_datetime corresponding to the the specific datetime period to be evaluated. - The input: bus_line or input: bus_line_id is provided to the function, so as timetables for the specific bus_line to be generated. :param timetables_starting_datetime: datetime :param timetables_ending_datetime: datetime :param requests_min_departure_datetime: datetime :param requests_max_departure_datetime: datetime :param bus_line: bus_line_document :param bus_line_id: int :return: None """ maximum_timetable_id_in_database = self.mongodb_database_connection.get_maximum_or_minimum( collection='timetable' ) # 1: The list of bus_stops corresponding to the provided bus_line is retrieved. # # bus_stop_document: { # '_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'} # } # bus_stops: [bus_stop_document] # if bus_line is None and bus_line_id is None: return None elif bus_line is None: bus_line = self.mongodb_database_connection.find_bus_line_document(bus_line_id=bus_line_id) else: bus_line_id = bus_line.get('bus_line_id') bus_stops = bus_line.get('bus_stops') # 2: The Look Ahead retrieves from the System Database the travel_requests with # departure_datetime higher than requests_min_departure_datetime and # lower than requests_max_departure_datetime. # # travel_request_document: { # '_id', 'client_id', 'bus_line_id', # 'starting_bus_stop': {'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}, # 'ending_bus_stop': {'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}, # 'departure_datetime', 'arrival_datetime' # } # travel_requests: [travel_request_document] # travel_requests = self.mongodb_database_connection.find_travel_request_documents( bus_line_ids=[bus_line_id], min_departure_datetime=requests_min_departure_datetime, max_departure_datetime=requests_max_departure_datetime ) # 3: (TimetableGenerator is initialized) The Look Ahead sends a request to the Route Generator so as # to identify the less time-consuming bus_route between the bus_stops of bus_line, # while taking into consideration the current levels of traffic density. # timetable_generator = TimetableGenerator( maximum_timetable_id_in_database=maximum_timetable_id_in_database, bus_line_id=bus_line_id, bus_stops=bus_stops, travel_requests=travel_requests ) # The list of bus_stops of a bus_line might contain the same bus_stop_osm_ids more than once. # For this reason, each travel_request needs to be related with the correct index in the bus_stops list. # So, the values 'starting_timetable_entry_index' and 'ending_timetable_entry_index' are estimated. # correspond_travel_requests_to_bus_stops( travel_requests=timetable_generator.travel_requests, bus_stops=timetable_generator.bus_stops ) # 4: Based on the response of the Route Generator, which includes details about the followed bus_route, # and using only one bus vehicle, the Look Ahead generates some initial timetables which cover the # whole datetime period from timetables_starting_datetime to timetables_ending_datetime. # Initially, the list of travel requests of these timetables is empty, and the departure_datetime and # arrival_datetime values of the timetable_entries are based exclusively on the details of the bus_route. # In the next steps of the algorithm, these timetables are used in the initial clustering # of the travel requests. # timetable_generator.timetables = generate_initial_timetables( bus_line_id=bus_line_id, timetables_starting_datetime=timetables_starting_datetime, timetables_ending_datetime=timetables_ending_datetime, route_generator_response=timetable_generator.route_generator_response ) current_average_waiting_time_of_timetables = float('Inf') while True: new_timetables = generate_new_timetables_based_on_travel_requests( current_timetables=timetable_generator.timetables, travel_requests=timetable_generator.travel_requests ) new_average_waiting_time_of_timetables = calculate_average_waiting_time_of_timetables_in_seconds( timetables=new_timetables ) if new_average_waiting_time_of_timetables < current_average_waiting_time_of_timetables: timetable_generator.timetables = new_timetables current_average_waiting_time_of_timetables = new_average_waiting_time_of_timetables print_timetables(timetables=timetable_generator.timetables) else: break print_timetables(timetables=timetable_generator.timetables) self.mongodb_database_connection.delete_timetable_documents( bus_line_id=bus_line.get('bus_line_id') ) self.mongodb_database_connection.insert_timetable_documents( timetable_documents=timetable_generator.timetables ) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='insert_timetable_documents (mongodb_database): ok') def generate_timetables_for_bus_lines(self, timetables_starting_datetime, timetables_ending_datetime, requests_min_departure_datetime, requests_max_departure_datetime): """ Generate timetables for all bus_lines, for a selected datetime period, evaluating travel_requests of a specific datetime period. :param timetables_starting_datetime: datetime :param timetables_ending_datetime: datetime :param requests_min_departure_datetime: datetime :param requests_max_departure_datetime: datetime :return: None """ bus_lines = self.mongodb_database_connection.find_bus_line_documents() for bus_line in bus_lines: self.generate_timetables_for_bus_line( bus_line=bus_line, timetables_starting_datetime=timetables_starting_datetime, timetables_ending_datetime=timetables_ending_datetime, requests_min_departure_datetime=requests_min_departure_datetime, requests_max_departure_datetime=requests_max_departure_datetime ) def update_timetables_of_bus_line(self, bus_line=None, bus_line_id=None): """ Update the timetables of a bus_line, taking into consideration the current levels of traffic_density. :param bus_line: bus_line_document :param bus_line_id: int :return: None """ if bus_line is None and bus_line_id is None: return None elif bus_line is None: bus_line = self.mongodb_database_connection.find_bus_line_document(bus_line_id=bus_line_id) else: bus_line_id = bus_line.get('bus_line_id') bus_stops = bus_line.get('bus_stops') timetables = self.mongodb_database_connection.find_timetable_documents(bus_line_ids=[bus_line_id]) travel_requests = get_travel_requests_of_timetables(timetables=timetables) timetable_updater = TimetableUpdater( bus_stops=bus_stops, timetables=timetables, travel_requests=travel_requests ) update_entries_of_timetables( timetables=timetable_updater.timetables, route_generator_response=timetable_updater.route_generator_response ) current_average_waiting_time_of_timetables = calculate_average_waiting_time_of_timetables_in_seconds( timetables=timetable_updater.timetables ) print_timetables(timetables=timetable_updater.timetables) while True: new_timetables = generate_new_timetables_based_on_travel_requests( current_timetables=timetable_updater.timetables, travel_requests=timetable_updater.travel_requests ) new_average_waiting_time_of_timetables = calculate_average_waiting_time_of_timetables_in_seconds( timetables=new_timetables ) if new_average_waiting_time_of_timetables < current_average_waiting_time_of_timetables: timetable_updater.timetables = new_timetables current_average_waiting_time_of_timetables = new_average_waiting_time_of_timetables print_timetables(timetables=timetable_updater.timetables) else: break print_timetables(timetables=timetable_updater.timetables) self.mongodb_database_connection.delete_timetable_documents( bus_line_id=bus_line.get('bus_line_id') ) self.mongodb_database_connection.insert_timetable_documents( timetable_documents=timetable_updater.timetables ) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='update_timetable_documents (mongodb_database): ok') def update_timetables_of_bus_lines(self): """ Update the timetables of all bus_lines, taking into consideration the current levels of traffic_density. :return: None """ bus_lines = self.mongodb_database_connection.find_bus_line_documents() for bus_line in bus_lines: self.update_timetables_of_bus_line(bus_line=bus_line)
class LookAheadHandler(object): def __init__(self): self.mongodb_database_connection = MongodbDatabaseConnection( host=mongodb_host, port=mongodb_port) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='mongodb_database_connection: established') def generate_bus_line(self, bus_stop_names, bus_line_id=None): """ Generate a bus_line, consisted of a bus_line_id and a list of bus_stops, and store it to the corresponding collection of the System Database. Moreover, identify all the possible waypoints between the bus_stops of the bus_line, and populate the BusStopWaypoints collection. :param bus_stop_names: [string] :param bus_line_id: int :return: None """ # 1: The inputs: bus_line_id and bus_stop_names are provided to the function, so as as a bus_line # with the corresponding bus_line_id and bus_stops to be generated. # # 2: The Look Ahead connects to the System Database and retrieves the bus_stops which correspond to # the provided bus_stop_names. The function returns None and the bus_line is not generated, # in case there is a bus_stop_name which does not correspond to a stored bus_stop. # if bus_line_id is None: maximum_bus_line_id = self.mongodb_database_connection.get_maximum_or_minimum( collection='bus_line') bus_line_id = maximum_bus_line_id + 1 bus_stops = [] for bus_stop_name in bus_stop_names: bus_stop_document = self.mongodb_database_connection.find_bus_stop_document( name=bus_stop_name) if bus_stop_document is None: log_message = 'find_bus_stop_document (mongodb_database) - name:', bus_stop_name, '- result: None' log(module_name='look_ahead_handler', log_type='DEBUG', log_message=log_message) return None else: bus_stops.append(bus_stop_document) # 3: The intermediate waypoints of the bus_routes, which are generated while combining starting and # ending bus_stops of the bus_line, should be stored as bus_stop_waypoints_documents at the System Database. # The Look Ahead checks the existing bus_stop_waypoints_documents and communicates with the Route Generator # in order to identify the waypoints of the bus_routes which are not already stored. The newly generated # bus_stop_waypoints_documents are getting stored to the corresponding collection of the System Database. # The function returns None and the bus_line is not generated, in case the Route Generator can not identify # a possible route in order to connect the bus_stops of the bus_line. # number_of_bus_stops = len(bus_stops) for i in range(0, number_of_bus_stops - 1): starting_bus_stop = bus_stops[i] ending_bus_stop = bus_stops[i + 1] bus_stop_waypoints_document = self.mongodb_database_connection.find_bus_stop_waypoints_document( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop) if bus_stop_waypoints_document is None: route_generator_response = get_waypoints_between_two_bus_stops( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop) if route_generator_response is None: log(module_name='look_ahead_handler', log_type='DEBUG', log_message= 'get_waypoints_between_two_bus_stops (route_generator): None' ) return None else: waypoints = route_generator_response.get('waypoints') if len(waypoints) == 0: log(module_name='look_ahead_handler', log_type='DEBUG', log_message= 'get_waypoints_between_two_bus_stops (route_generator): None' ) return None lists_of_edge_object_ids = [] for list_of_edges in waypoints: list_of_edge_object_ids = [] for edge in list_of_edges: edge_object_id = edge.get('_id') list_of_edge_object_ids.append(edge_object_id) lists_of_edge_object_ids.append( list_of_edge_object_ids) # waypoints: [[edge_object_id]] # waypoints = lists_of_edge_object_ids self.mongodb_database_connection.insert_bus_stop_waypoints_document( starting_bus_stop=starting_bus_stop, ending_bus_stop=ending_bus_stop, waypoints=waypoints) # 4: The Look Ahead stores the newly generated bus_line_document, which is consisted of the bus_line_id # and the list of bus_stops, to the corresponding collection of the System Database. # In case there is an already existing bus_line_document, with the same bus_line_id, # then the list of bus_stops gets updated. # bus_line_document = { 'bus_line_id': bus_line_id, 'bus_stops': bus_stops } self.mongodb_database_connection.insert_bus_line_document( bus_line_document=bus_line_document) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='insert_bus_line_document (mongodb_database): ok') def generate_timetables_for_bus_line(self, timetables_starting_datetime, timetables_ending_datetime, requests_min_departure_datetime, requests_max_departure_datetime, bus_line=None, bus_line_id=None): """ Generate timetables for a bus_line, for a selected datetime period, evaluating travel_requests of a specific datetime period. - The input: timetables_starting_datetime and input: timetables_ending_datetime are provided to the function, so as timetables for the specific datetime period to be generated. - The input: requests_min_departure_datetime and input: requests_max_departure_datetime are provided to the function, so as travel_requests with departure_datetime corresponding to the the specific datetime period to be evaluated. - The input: bus_line or input: bus_line_id is provided to the function, so as timetables for the specific bus_line to be generated. :param timetables_starting_datetime: datetime :param timetables_ending_datetime: datetime :param requests_min_departure_datetime: datetime :param requests_max_departure_datetime: datetime :param bus_line: bus_line_document :param bus_line_id: int :return: None """ maximum_timetable_id_in_database = self.mongodb_database_connection.get_maximum_or_minimum( collection='timetable') # 1: The list of bus_stops corresponding to the provided bus_line is retrieved. # # bus_stop_document: { # '_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'} # } # bus_stops: [bus_stop_document] # if bus_line is None and bus_line_id is None: return None elif bus_line is None: bus_line = self.mongodb_database_connection.find_bus_line_document( bus_line_id=bus_line_id) else: bus_line_id = bus_line.get('bus_line_id') bus_stops = bus_line.get('bus_stops') # 2: The Look Ahead retrieves from the System Database the travel_requests with # departure_datetime higher than requests_min_departure_datetime and # lower than requests_max_departure_datetime. # # travel_request_document: { # '_id', 'client_id', 'bus_line_id', # 'starting_bus_stop': {'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}, # 'ending_bus_stop': {'_id', 'osm_id', 'name', 'point': {'longitude', 'latitude'}}, # 'departure_datetime', 'arrival_datetime' # } # travel_requests: [travel_request_document] # travel_requests = self.mongodb_database_connection.find_travel_request_documents( bus_line_ids=[bus_line_id], min_departure_datetime=requests_min_departure_datetime, max_departure_datetime=requests_max_departure_datetime) # 3: (TimetableGenerator is initialized) The Look Ahead sends a request to the Route Generator so as # to identify the less time-consuming bus_route between the bus_stops of bus_line, # while taking into consideration the current levels of traffic density. # timetable_generator = TimetableGenerator( maximum_timetable_id_in_database=maximum_timetable_id_in_database, bus_line_id=bus_line_id, bus_stops=bus_stops, travel_requests=travel_requests) # The list of bus_stops of a bus_line might contain the same bus_stop_osm_ids more than once. # For this reason, each travel_request needs to be related with the correct index in the bus_stops list. # So, the values 'starting_timetable_entry_index' and 'ending_timetable_entry_index' are estimated. # correspond_travel_requests_to_bus_stops( travel_requests=timetable_generator.travel_requests, bus_stops=timetable_generator.bus_stops) # 4: Based on the response of the Route Generator, which includes details about the followed bus_route, # and using only one bus vehicle, the Look Ahead generates some initial timetables which cover the # whole datetime period from timetables_starting_datetime to timetables_ending_datetime. # Initially, the list of travel requests of these timetables is empty, and the departure_datetime and # arrival_datetime values of the timetable_entries are based exclusively on the details of the bus_route. # In the next steps of the algorithm, these timetables are used in the initial clustering # of the travel requests. # timetable_generator.timetables = generate_initial_timetables( bus_line_id=bus_line_id, timetables_starting_datetime=timetables_starting_datetime, timetables_ending_datetime=timetables_ending_datetime, route_generator_response=timetable_generator. route_generator_response) current_average_waiting_time_of_timetables = float('Inf') while True: new_timetables = generate_new_timetables_based_on_travel_requests( current_timetables=timetable_generator.timetables, travel_requests=timetable_generator.travel_requests) new_average_waiting_time_of_timetables = calculate_average_waiting_time_of_timetables_in_seconds( timetables=new_timetables) if new_average_waiting_time_of_timetables < current_average_waiting_time_of_timetables: timetable_generator.timetables = new_timetables current_average_waiting_time_of_timetables = new_average_waiting_time_of_timetables print_timetables(timetables=timetable_generator.timetables) else: break print_timetables(timetables=timetable_generator.timetables) self.mongodb_database_connection.delete_timetable_documents( bus_line_id=bus_line.get('bus_line_id')) self.mongodb_database_connection.insert_timetable_documents( timetable_documents=timetable_generator.timetables) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='insert_timetable_documents (mongodb_database): ok') def generate_timetables_for_bus_lines(self, timetables_starting_datetime, timetables_ending_datetime, requests_min_departure_datetime, requests_max_departure_datetime): """ Generate timetables for all bus_lines, for a selected datetime period, evaluating travel_requests of a specific datetime period. :param timetables_starting_datetime: datetime :param timetables_ending_datetime: datetime :param requests_min_departure_datetime: datetime :param requests_max_departure_datetime: datetime :return: None """ bus_lines = self.mongodb_database_connection.find_bus_line_documents() for bus_line in bus_lines: self.generate_timetables_for_bus_line( bus_line=bus_line, timetables_starting_datetime=timetables_starting_datetime, timetables_ending_datetime=timetables_ending_datetime, requests_min_departure_datetime=requests_min_departure_datetime, requests_max_departure_datetime=requests_max_departure_datetime ) def update_timetables_of_bus_line(self, bus_line=None, bus_line_id=None): """ Update the timetables of a bus_line, taking into consideration the current levels of traffic_density. :param bus_line: bus_line_document :param bus_line_id: int :return: None """ if bus_line is None and bus_line_id is None: return None elif bus_line is None: bus_line = self.mongodb_database_connection.find_bus_line_document( bus_line_id=bus_line_id) else: bus_line_id = bus_line.get('bus_line_id') bus_stops = bus_line.get('bus_stops') timetables = self.mongodb_database_connection.find_timetable_documents( bus_line_ids=[bus_line_id]) travel_requests = get_travel_requests_of_timetables( timetables=timetables) timetable_updater = TimetableUpdater(bus_stops=bus_stops, timetables=timetables, travel_requests=travel_requests) update_entries_of_timetables( timetables=timetable_updater.timetables, route_generator_response=timetable_updater.route_generator_response ) current_average_waiting_time_of_timetables = calculate_average_waiting_time_of_timetables_in_seconds( timetables=timetable_updater.timetables) print_timetables(timetables=timetable_updater.timetables) while True: new_timetables = generate_new_timetables_based_on_travel_requests( current_timetables=timetable_updater.timetables, travel_requests=timetable_updater.travel_requests) new_average_waiting_time_of_timetables = calculate_average_waiting_time_of_timetables_in_seconds( timetables=new_timetables) if new_average_waiting_time_of_timetables < current_average_waiting_time_of_timetables: timetable_updater.timetables = new_timetables current_average_waiting_time_of_timetables = new_average_waiting_time_of_timetables print_timetables(timetables=timetable_updater.timetables) else: break print_timetables(timetables=timetable_updater.timetables) self.mongodb_database_connection.delete_timetable_documents( bus_line_id=bus_line.get('bus_line_id')) self.mongodb_database_connection.insert_timetable_documents( timetable_documents=timetable_updater.timetables) log(module_name='look_ahead_handler', log_type='DEBUG', log_message='update_timetable_documents (mongodb_database): ok') def update_timetables_of_bus_lines(self): """ Update the timetables of all bus_lines, taking into consideration the current levels of traffic_density. :return: None """ bus_lines = self.mongodb_database_connection.find_bus_line_documents() for bus_line in bus_lines: self.update_timetables_of_bus_line(bus_line=bus_line)