Spaces:
Running on Zero
Running on Zero
Download src/integration/sumo_phase_adapter.py from vish07anth/ATA: direct link, hf CLI and curl.
- Browser
- Download file 11.2 kB
-
https://huggingface.co/spaces/vish07anth/ATA/resolve/main/src/integration/sumo_phase_adapter.py
- Command line
-
hf download hf://spaces/vish07anth/ATA/src/integration/sumo_phase_adapter.py
-
curl -L -o sumo_phase_adapter.py https://huggingface.co/spaces/vish07anth/ATA/resolve/main/src/integration/sumo_phase_adapter.py
11.2 kB
| try: | |
| import traci | |
| except ImportError: | |
| traci = None | |
| class SUMOPhaseAdapter: | |
| """ | |
| Converts SUMO traffic-light phases into a generic | |
| representation that can be used by the decision engine. | |
| SUMO-specific link indices are kept here instead of | |
| being placed inside the generic topology classes. | |
| """ | |
| def __init__(self, traci_connection=None): | |
| self.traci = traci_connection or traci | |
| # ============================================================ | |
| # GET ALL SIGNAL PROGRAMS | |
| # ============================================================ | |
| def get_programs(self, junction_id): | |
| """ | |
| Return all signal programs available at a junction. | |
| """ | |
| programs = ( | |
| self.traci.trafficlight.getAllProgramLogics( | |
| junction_id | |
| ) | |
| ) | |
| result = [] | |
| for program in programs: | |
| program_data = { | |
| "program_id": program.programID, | |
| "type": program.type, | |
| "current_phase": ( | |
| self.traci.trafficlight.getPhase( | |
| junction_id | |
| ) | |
| ), | |
| "phases": [] | |
| } | |
| for phase_index, phase in enumerate( | |
| program.phases | |
| ): | |
| program_data["phases"].append({ | |
| "phase_index": phase_index, | |
| "duration": phase.duration, | |
| "min_duration": phase.minDur, | |
| "max_duration": phase.maxDur, | |
| "state": phase.state | |
| }) | |
| result.append(program_data) | |
| return result | |
| # ============================================================ | |
| # GET CURRENT PROGRAM | |
| # ============================================================ | |
| def get_current_program(self, junction_id): | |
| """ | |
| Get the currently active SUMO signal program. | |
| """ | |
| program_id = ( | |
| self.traci.trafficlight.getProgram( | |
| junction_id | |
| ) | |
| ) | |
| programs = self.get_programs(junction_id) | |
| for program in programs: | |
| if program["program_id"] == program_id: | |
| return program | |
| return None | |
| # ============================================================ | |
| # GET CURRENT PHASE | |
| # ============================================================ | |
| def get_current_phase(self, junction_id): | |
| """ | |
| Return the current phase index. | |
| """ | |
| return self.traci.trafficlight.getPhase( | |
| junction_id | |
| ) | |
| # ============================================================ | |
| # GET PHASE STATE | |
| # ============================================================ | |
| def get_phase_state( | |
| self, | |
| junction_id, | |
| phase_index | |
| ): | |
| """ | |
| Return the signal state string for a phase. | |
| Example: | |
| GGggrrrrGGggrrrr | |
| """ | |
| program = self.get_current_program( | |
| junction_id | |
| ) | |
| if program is None: | |
| return None | |
| for phase in program["phases"]: | |
| if phase["phase_index"] == phase_index: | |
| return phase["state"] | |
| return None | |
| # ============================================================ | |
| # GET GREEN LINKS | |
| # ============================================================ | |
| def get_green_links( | |
| self, | |
| junction_id, | |
| phase_index | |
| ): | |
| """ | |
| Return link indices that are green during | |
| the specified phase. | |
| SUMO signal characters: | |
| G = green | |
| g = permissive green | |
| y = yellow | |
| r = red | |
| Both G and g are considered active green. | |
| """ | |
| state = self.get_phase_state( | |
| junction_id, | |
| phase_index | |
| ) | |
| if state is None: | |
| return [] | |
| green_links = [] | |
| for link_index, signal in enumerate(state): | |
| if signal in ("G", "g"): | |
| green_links.append( | |
| link_index | |
| ) | |
| return green_links | |
| # ============================================================ | |
| # GET PHASE TYPE | |
| # ============================================================ | |
| def get_phase_type( | |
| self, | |
| junction_id, | |
| phase_index | |
| ): | |
| """ | |
| Classify a phase as: | |
| green | |
| yellow | |
| red | |
| mixed | |
| """ | |
| state = self.get_phase_state( | |
| junction_id, | |
| phase_index | |
| ) | |
| if state is None: | |
| return "unknown" | |
| has_green = any( | |
| signal in ("G", "g") | |
| for signal in state | |
| ) | |
| has_yellow = any( | |
| signal == "y" | |
| for signal in state | |
| ) | |
| has_red = any( | |
| signal == "r" | |
| for signal in state | |
| ) | |
| if has_green: | |
| return "green" | |
| if has_yellow: | |
| return "yellow" | |
| if has_red: | |
| return "red" | |
| return "mixed" | |
| # ============================================================ | |
| # GET DECISION PHASES | |
| # ============================================================ | |
| def get_decision_phases(self, junction_id): | |
| """ | |
| Return phases that can be considered normal | |
| green decision phases. | |
| Yellow phases are deliberately excluded. | |
| """ | |
| program = self.get_current_program( | |
| junction_id | |
| ) | |
| if program is None: | |
| return [] | |
| decision_phases = [] | |
| for phase in program["phases"]: | |
| phase_index = phase["phase_index"] | |
| phase_type = self.get_phase_type( | |
| junction_id, | |
| phase_index | |
| ) | |
| if phase_type == "green": | |
| decision_phases.append({ | |
| "phase_index": phase_index, | |
| "duration": phase["duration"], | |
| "state": phase["state"], | |
| "green_links": ( | |
| self.get_green_links( | |
| junction_id, | |
| phase_index | |
| ) | |
| ) | |
| }) | |
| return decision_phases | |
| # ============================================================ | |
| # CONVERT PHASE TO MOVEMENTS | |
| # ============================================================ | |
| def phase_to_movements( | |
| self, | |
| junction_id, | |
| phase_index, | |
| movement_metadata | |
| ): | |
| """ | |
| Convert SUMO green link indices into movement IDs. | |
| movement_metadata should be the metadata produced | |
| by SUMOTopologyAdapter. | |
| Example: | |
| link 0 | |
| ↓ | |
| A0_M0 | |
| link 1 | |
| ↓ | |
| A0_M1 | |
| """ | |
| green_links = self.get_green_links( | |
| junction_id, | |
| phase_index | |
| ) | |
| movements = [] | |
| for movement_id, metadata in ( | |
| movement_metadata.items() | |
| ): | |
| if ( | |
| metadata["junction_id"] == junction_id | |
| and metadata["link_index"] | |
| in green_links | |
| ): | |
| movements.append( | |
| movement_id | |
| ) | |
| return movements | |
| # ============================================================ | |
| # BUILD PHASE REPRESENTATION | |
| # ============================================================ | |
| def build_phase_representation( | |
| self, | |
| junction_id, | |
| movement_metadata | |
| ): | |
| """ | |
| Build a generic representation of all usable | |
| signal phases at a junction. | |
| """ | |
| decision_phases = ( | |
| self.get_decision_phases( | |
| junction_id | |
| ) | |
| ) | |
| phases = [] | |
| for phase in decision_phases: | |
| phase_index = phase[ | |
| "phase_index" | |
| ] | |
| movement_ids = ( | |
| self.phase_to_movements( | |
| junction_id, | |
| phase_index, | |
| movement_metadata | |
| ) | |
| ) | |
| phases.append({ | |
| "phase_id": ( | |
| f"{junction_id}_P" | |
| f"{phase_index}" | |
| ), | |
| "junction_id": junction_id, | |
| "phase_index": phase_index, | |
| "duration": phase["duration"], | |
| "state": phase["state"], | |
| "green_links": phase[ | |
| "green_links" | |
| ], | |
| "movements": movement_ids | |
| }) | |
| return phases | |
| # ============================================================ | |
| # PRINT DEBUG INFORMATION | |
| # ============================================================ | |
| def print_junction_phases( | |
| self, | |
| junction_id, | |
| movement_metadata | |
| ): | |
| """ | |
| Print a readable representation for debugging. | |
| """ | |
| print() | |
| print("=" * 70) | |
| print( | |
| f"PHASES FOR JUNCTION: {junction_id}" | |
| ) | |
| print("=" * 70) | |
| phases = ( | |
| self.build_phase_representation( | |
| junction_id, | |
| movement_metadata | |
| ) | |
| ) | |
| for phase in phases: | |
| print() | |
| print( | |
| f"Phase: {phase['phase_id']}" | |
| ) | |
| print( | |
| f" SUMO index: " | |
| f"{phase['phase_index']}" | |
| ) | |
| print( | |
| f" Duration: " | |
| f"{phase['duration']} seconds" | |
| ) | |
| print( | |
| f" State: " | |
| f"{phase['state']}" | |
| ) | |
| print( | |
| f" Green links: " | |
| f"{phase['green_links']}" | |
| ) | |
| print( | |
| f" Movements: " | |
| f"{phase['movements']}" | |
| ) | |
| # ================================================================ | |
| # TEST | |
| # ================================================================ | |
| if __name__ == "__main__": | |
| SUMO_CMD = [ | |
| "sumo", | |
| "-c", | |
| "simulation/configs/corridor.sumocfg" | |
| ] | |
| try: | |
| traci.start(SUMO_CMD) | |
| from integration.sumo_topology_adapter import ( | |
| SUMOTopologyAdapter | |
| ) | |
| topology_adapter = ( | |
| SUMOTopologyAdapter() | |
| ) | |
| phase_adapter = ( | |
| SUMOPhaseAdapter() | |
| ) | |
| # Discover topology first. | |
| topologies = ( | |
| topology_adapter | |
| .discover_all_junctions() | |
| ) | |
| # Display phases for the main | |
| # corridor junctions. | |
| for junction_id in [ | |
| "A0", | |
| "B0", | |
| "C0" | |
| ]: | |
| phase_adapter.print_junction_phases( | |
| junction_id, | |
| topology_adapter.sumo_metadata | |
| ) | |
| finally: | |
| traci.close() |