Delivery_robot / app /simulator.py
minhnghiem32131024429
Deploy delivery robot app
5e8b911
Raw
History Blame Contribute Delete
5.7 kB
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)