Spaces:
Sleeping
Sleeping
| import asyncio | |
| import logging | |
| import app.state as state | |
| logger = logging.getLogger(__name__) | |
| # Constants matching ESP_Code.ino FSM timings (in seconds) | |
| TIME_PER_FLOOR = 2.0 | |
| TIME_DOOR_OPENING = 1.5 | |
| TIME_DOORS_OPEN = 4.0 | |
| TIME_DOOR_CLOSING = 1.5 | |
| async def run_elevator_simulator(): | |
| """ | |
| Virtual Elevator Simulator that runs as a background task. | |
| Monitors state.elevator_cmd_event, updates state.elevator_state, | |
| and broadcasts state transitions via Server-Sent Events (SSE). | |
| """ | |
| logger.info("Virtual Elevator Simulator started") | |
| # Initialize state | |
| state.elevator_state.Floor = 1 | |
| state.elevator_state.Dir = "IDLE" | |
| state.elevator_state.Doors = "CLOSED" | |
| state.elevator_state.State = "IDLE" | |
| while True: | |
| try: | |
| # Wait until a command is set | |
| await state.elevator_cmd_event.wait() | |
| cmd = state.elevator_cmd | |
| if not cmd or not cmd.startswith("CALL_"): | |
| state.elevator_cmd_event.clear() | |
| await asyncio.sleep(0.1) | |
| continue | |
| try: | |
| target_floor = int(cmd.split("_")[1]) | |
| except (IndexError, ValueError): | |
| logger.error("Invalid elevator command format: %s", cmd) | |
| state.elevator_cmd = None | |
| state.elevator_cmd_event.clear() | |
| continue | |
| current_floor = state.elevator_state.Floor | |
| if target_floor == current_floor: | |
| logger.info("Elevator is already at target floor %d", target_floor) | |
| state.elevator_cmd = None | |
| state.elevator_cmd_event.clear() | |
| continue | |
| # Consume the command | |
| state.elevator_cmd = None | |
| state.elevator_cmd_event.clear() | |
| # 1. State: MOVING | |
| state.elevator_state.State = "MOVING" | |
| state.elevator_state.Dir = "UP" if target_floor > current_floor else "DOWN" | |
| state.elevator_state.Doors = "CLOSED" | |
| logger.info("Elevator moving from floor %d to %d (%s)", current_floor, target_floor, state.elevator_state.Dir) | |
| await state.broadcast("elevator", { | |
| "Floor": state.elevator_state.Floor, | |
| "Dir": state.elevator_state.Dir, | |
| "Doors": state.elevator_state.Doors, | |
| "State": state.elevator_state.State, | |
| }) | |
| # Move floor-by-floor | |
| step = 1 if target_floor > current_floor else -1 | |
| while state.elevator_state.Floor != target_floor: | |
| await asyncio.sleep(TIME_PER_FLOOR) | |
| state.elevator_state.Floor += step | |
| logger.info("Elevator reached floor %d", state.elevator_state.Floor) | |
| await state.broadcast("elevator", { | |
| "Floor": state.elevator_state.Floor, | |
| "Dir": state.elevator_state.Dir, | |
| "Doors": state.elevator_state.Doors, | |
| "State": state.elevator_state.State, | |
| }) | |
| # 2. State: DOORS_OPENING | |
| state.elevator_state.Dir = "IDLE" | |
| state.elevator_state.State = "DOORS_OPENING" | |
| state.elevator_state.Doors = "OPENING" | |
| logger.info("Elevator arrived at floor %d. Opening doors...", state.elevator_state.Floor) | |
| await state.broadcast("elevator", { | |
| "Floor": state.elevator_state.Floor, | |
| "Dir": state.elevator_state.Dir, | |
| "Doors": state.elevator_state.Doors, | |
| "State": state.elevator_state.State, | |
| }) | |
| await asyncio.sleep(TIME_DOOR_OPENING) | |
| # 3. State: DOORS_OPEN | |
| state.elevator_state.State = "DOORS_OPEN" | |
| state.elevator_state.Doors = "OPEN" | |
| logger.info("Elevator doors are OPEN at floor %d", state.elevator_state.Floor) | |
| await state.broadcast("elevator", { | |
| "Floor": state.elevator_state.Floor, | |
| "Dir": state.elevator_state.Dir, | |
| "Doors": state.elevator_state.Doors, | |
| "State": state.elevator_state.State, | |
| }) | |
| await asyncio.sleep(TIME_DOORS_OPEN) | |
| # 4. State: DOORS_CLOSING | |
| state.elevator_state.State = "DOORS_CLOSING" | |
| state.elevator_state.Doors = "CLOSING" | |
| logger.info("Elevator doors are CLOSING at floor %d", state.elevator_state.Floor) | |
| await state.broadcast("elevator", { | |
| "Floor": state.elevator_state.Floor, | |
| "Dir": state.elevator_state.Dir, | |
| "Doors": state.elevator_state.Doors, | |
| "State": state.elevator_state.State, | |
| }) | |
| await asyncio.sleep(TIME_DOOR_CLOSING) | |
| # 5. State: IDLE / CLOSED | |
| state.elevator_state.State = "IDLE" | |
| state.elevator_state.Doors = "CLOSED" | |
| logger.info("Elevator is now IDLE at floor %d", state.elevator_state.Floor) | |
| await state.broadcast("elevator", { | |
| "Floor": state.elevator_state.Floor, | |
| "Dir": state.elevator_state.Dir, | |
| "Doors": state.elevator_state.Doors, | |
| "State": state.elevator_state.State, | |
| }) | |
| except asyncio.CancelledError: | |
| logger.info("Elevator simulator task cancelled") | |
| break | |
| except Exception as e: | |
| logger.error("Exception in elevator simulator: %s", e, exc_info=True) | |
| await asyncio.sleep(1.0) | |