diff --git a/scripts/insert_demonstration_simulation.py b/scripts/insert_demonstration_simulation.py index 3534baec..64686953 100644 --- a/scripts/insert_demonstration_simulation.py +++ b/scripts/insert_demonstration_simulation.py @@ -29,10 +29,11 @@ strategy_type="RandomScheduleStrategy", strategy_start_time=0, strategy_end_time=7200, + train_schedule_train_type="cargo", random_strategy_trains_per_1000_seconds=2.0, ) print(f"regular schedule: {regular_schedule.id}") - print(f"radom schedule: {random_schedule.id}") + print(f"random schedule: {random_schedule.id}") for index, platform in enumerate(platforms): ScheduleConfigurationXSimulationPlatform.create( schedule_configuration_id=regular_schedule, diff --git a/src/interlocking_component/route_controller.py b/src/interlocking_component/route_controller.py index 225c3cd5..36d5326b 100644 --- a/src/interlocking_component/route_controller.py +++ b/src/interlocking_component/route_controller.py @@ -108,14 +108,18 @@ class UninitializedTrain: When the spawnroute is set, the train is not yet initialized. """ - identifier: str = "/not_a_real_train" - reserved_tracks: List[Track] = None + identifier: str + reserved_tracks: List[ReservationTrack] = None reserved_until_station_index: int = 1 timetable: List[Platform] = None route: str = None _station_index: int = 1 + state: Train.State = Train.State.DRIVING - def __init__(self, timetable: List[Platform]): + def __init__( + self, timetable: List[Platform], identifier: str = "/not_a_real_train" + ): + self.identifier = identifier self.timetable = timetable self.reserved_tracks = [] @@ -189,7 +193,7 @@ class RouteController(Component): class RouteQueues: """This class capsules all routes, that need to be considered every tick.""" - routes_to_be_set: List[Route] + routes_to_be_set: List[Tuple[Train, Route]] routes_waiting_for_reservations: List[ Tuple[List[Node], Train, Route, List[Node]] ] @@ -235,12 +239,12 @@ def __init__( def next_tick(self, tick: int): self.tick = tick - for interlocking_route in self.route_queues.routes_to_be_set: + for train, interlocking_route in self.route_queues.routes_to_be_set: # This tries to set the fahrstrasse in the interlocking. # The Sumo route was already set and the route was reserved. was_set = self.set_interlocking_route(interlocking_route) if was_set: - self.route_queues.routes_to_be_set.remove(interlocking_route) + self.route_queues.routes_to_be_set.remove(train, interlocking_route) for ( route, train, @@ -269,9 +273,47 @@ def set_spawn_fahrstrasse( :raises KeyError: The route could not be found in the interlocking. :return: The id of the first SUMO Route and the placholder for reservations. """ - train_to_be_initialized = UninitializedTrain(timetable) + train_to_be_initialized = UninitializedTrain( + timetable, "/not_a_real_train_" + str(self.tick) + ) self.set_fahrstrasse(train_to_be_initialized, starting_edge) - return train_to_be_initialized.route, train_to_be_initialized + if train_to_be_initialized.state == Train.State.DRIVING: + return train_to_be_initialized.route, train_to_be_initialized + self.remove_train_from_queues(train_to_be_initialized) + self.remove_reservations_for_train(train_to_be_initialized) + return None, train_to_be_initialized + + def remove_train_from_queues(self, train_to_be_removed: Train): + """This method removes a train from all queues where fahrstrassen or + things like that may be set in the next_tick method + + :param train_to_be_removed: The train, that will not be considered anymore + """ + for ( + route, + train, + interlocking_route, + entire_route, + ) in self.route_queues.routes_waiting_for_reservations: + if train == train_to_be_removed: + self.route_queues.routes_waiting_for_reservations.remove( + (route, train, interlocking_route, entire_route) + ) + for train, interlocking_route in self.route_queues.routes_to_be_set: + if train == train_to_be_removed: + self.route_queues.routes_to_be_set.remove((train, interlocking_route)) + + def remove_reservations_for_train(self, train: Train): + """This Method removes all reservations made by a train. + It deletes them in the train and in the tracks. + + :param train: The train, that will have its reservations removed + """ + for track in train.reserved_tracks: + for reserved_train, edge in track.reservations: + if reserved_train == train: + track.reservations.remove((reserved_train, edge)) + train.reserved_tracks = [] def reserve_for_initialized_train( self, reservation_placeholder: UninitializedTrain, train: Train @@ -304,7 +346,6 @@ def maybe_set_fahrstrasse(self, train: Train, edge: Edge): """ if train.current_platform is None: # if the train has reached the last station, don't allocate a new fahrstraße - print("No new route needed") return routes = self._get_interlocking_routes_for_edge(edge) @@ -312,7 +353,6 @@ def maybe_set_fahrstrasse(self, train: Train, edge: Edge): if route.get_last_segment_of_route() != edge.identifier.split("-re")[0]: continue - print("Setting fahrstrasse.") self.set_fahrstrasse(train, edge) break @@ -363,6 +403,7 @@ def set_fahrstrasse(self, train: Train, edge: Edge): new_route, ) ) + train.state = Train.State.WAITING_FOR_RESERVATION return # If the no interlocking route is found an error is raised raise KeyError() @@ -388,10 +429,11 @@ def set_fahrstrasse_if_reservations_work( return False self.maybe_put_reservations_as_first(train, entire_route) - if self.check_if_route_is_reserved_as_first(route, train, entire_route): + if self.check_if_route_is_reserved_as_first(route, train): was_set = self.set_interlocking_route(interlocking_route) if not was_set: - self.route_queues.routes_to_be_set.append(interlocking_route) + self.route_queues.routes_to_be_set.append((train, interlocking_route)) + train.state = Train.State.WAITING_FOR_FAHRSTRASSE return True return False @@ -435,7 +477,7 @@ def check_if_route_is_reserved( return True def check_if_route_is_reserved_as_first( - self, route: List[Node], train: Train, entire_route: List[Node] + self, route: List[Node], train: Train ) -> bool: """This method checks, if the given route is fully reserved for the given train and if the train is in the first position in the queue. @@ -445,14 +487,14 @@ def check_if_route_is_reserved_as_first( :return: if the route is reserved for the train """ route_as_tracks = self.get_tracks_of_node_route(route) - entire_route_as_tracks = self.get_tracks_of_node_route(entire_route) - for i, track in enumerate(entire_route_as_tracks): - if not track.is_reservation_track: - continue - if len(track.reservations) == 0 or track.reservations[0][0] != train: - return False - if i >= len(route_as_tracks): + for i, reserved_track in enumerate(train.reserved_tracks): + if reserved_track not in route_as_tracks and i != 0: break + if ( + len(reserved_track.reservations) == 0 + or reserved_track.reservations[0][0] != train + ): + return False return True def reserve_route(self, route: List[Node], train: Train) -> bool: diff --git a/src/wrapper/simulation_objects.py b/src/wrapper/simulation_objects.py index 701f3e32..6c2e210f 100644 --- a/src/wrapper/simulation_objects.py +++ b/src/wrapper/simulation_objects.py @@ -780,13 +780,20 @@ def from_simulation( def add_simulation_connections(self) -> None: pass + class State(IntEnum): + """The possible states of the signal""" + + DRIVING = 1 + WAITING_FOR_RESERVATION = 2 + WAITING_FOR_FAHRSTRASSE = 3 + _position: Tuple[float, float] _route: str _edge: Edge _speed: float _timetable: List[Platform] + state: State = State.DRIVING _stop_state: bool - _station_index: int = 0 train_type: TrainType reserved_tracks: List[ReservationTrack] _station_index: int = 0