diff --git a/docs/events.rst b/docs/events.rst index 59c3673..c41f8eb 100644 --- a/docs/events.rst +++ b/docs/events.rst @@ -202,7 +202,7 @@ Event System Event Types ~~~~~~~~~~~ -The simulation generates 8 types of events defined in ``EventType`` enum: +The simulation generates 9 types of events defined in ``EventType`` enum: .. code-block:: python @@ -215,6 +215,7 @@ The simulation generates 8 types of events defined in ``EventType`` enum: IDLE = "idle" PASSENGER_BOARD = "passenger_board" PASSENGER_ALIGHT = "passenger_alight" + ELEVATOR_MOVE = "elevator_move" Event Generation ~~~~~~~~~~~~~~~~ @@ -260,6 +261,19 @@ Events are generated during tick processing: for elevator in self.elevators: # ... movement logic ... + # Elevator moves + if elevator.target_floor_direction != Direction.STOPPED: + self._emit_event( + EventType.ELEVATOR_MOVE, + { + "elevator": elevator.id, + "from_position": old_position, + "to_position": elevator.position.current_floor_float, + "direction": elevator.target_floor_direction.value, + "status": elevator.run_status.value, + } + ) + # Passing a floor if old_floor != new_floor and new_floor != target_floor: self._emit_event( @@ -363,6 +377,14 @@ The ``ElevatorController`` base class automatically routes events to handler met elevator = self.elevators[event.data["elevator"]] self.on_elevator_idle(elevator) + elif event.type == EventType.ELEVATOR_MOVE: + elevator = self.elevators[event.data["elevator"]] + from_position = event.data["from_position"] + to_position = event.data["to_position"] + direction = event.data["direction"] + status = event.data["status"] + self.on_elevator_move(elevator, from_position, to_position, direction, status) + # ... other event types ... Control Flow: Bus Example diff --git a/elevator_saga/client/base_controller.py b/elevator_saga/client/base_controller.py index 60b5510..5bee045 100644 --- a/elevator_saga/client/base_controller.py +++ b/elevator_saga/client/base_controller.py @@ -7,7 +7,7 @@ import os import time from abc import ABC, abstractmethod from pprint import pprint -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List from elevator_saga.client.api_client import ElevatorAPIClient from elevator_saga.client.proxy_models import ProxyElevator, ProxyFloor, ProxyPassenger @@ -171,6 +171,22 @@ class ElevatorController(ABC): """ pass + @abstractmethod + def on_elevator_move( + self, elevator: ProxyElevator, from_position: float, to_position: float, direction: str, status: str + ) -> None: + """ + 电梯移动时的回调 - 可选实现 + + Args: + elevator: 电梯代理对象 + from_position: 起始位置(浮点数表示楼层) + to_position: 目标位置(浮点数表示楼层) + direction: 移动方向 + status: 电梯运行状态 + """ + pass + def _internal_init(self, elevators: List[Any], floors: List[Any]) -> None: """内部初始化方法""" self.elevators = elevators @@ -218,7 +234,7 @@ class ElevatorController(ABC): # 获取初始状态并初始化,默认从0开始 try: state = self.api_client.get_state() - except ConnectionResetError as ex: + except ConnectionResetError as _: # noqa: F841 print(f"模拟器可能并没有开启,请检查模拟器是否启动 {self.api_client.base_url}") os._exit(1) if state.tick > 0: @@ -381,6 +397,22 @@ class ElevatorController(ABC): floor_proxy = ProxyFloor(floor_id, self.api_client) self.on_passenger_alight(elevator_proxy, passenger_proxy, floor_proxy) + elif event.type == EventType.ELEVATOR_MOVE: + elevator_id = event.data.get("elevator") + from_position = event.data.get("from_position") + to_position = event.data.get("to_position") + direction = event.data.get("direction") + status = event.data.get("status") + if ( + elevator_id is not None + and from_position is not None + and to_position is not None + and direction is not None + and status is not None + ): + elevator_proxy = ProxyElevator(elevator_id, self.api_client) + self.on_elevator_move(elevator_proxy, from_position, to_position, direction, status) + def _reset_and_reinit(self) -> None: """重置并重新初始化""" try: diff --git a/elevator_saga/client_examples/bus_example.py b/elevator_saga/client_examples/bus_example.py index 9978785..a9b99e4 100644 --- a/elevator_saga/client_examples/bus_example.py +++ b/elevator_saga/client_examples/bus_example.py @@ -72,6 +72,11 @@ class ElevatorBusExampleController(ElevatorController): def on_elevator_approaching(self, elevator: ProxyElevator, floor: ProxyFloor, direction: str) -> None: pass + def on_elevator_move( + self, elevator: ProxyElevator, from_position: float, to_position: float, direction: str, status: str + ) -> None: + pass + if __name__ == "__main__": algorithm = ElevatorBusExampleController() diff --git a/elevator_saga/client_examples/simple_example.py b/elevator_saga/client_examples/simple_example.py index 09a726b..041ebe3 100644 --- a/elevator_saga/client_examples/simple_example.py +++ b/elevator_saga/client_examples/simple_example.py @@ -139,6 +139,17 @@ class ElevatorBusController(ElevatorController): elevator.go_to_floor(elevator.target_floor + 1, immediate=True) print(f" 不让0号电梯上行停站,设定新目标楼层 {elevator.target_floor + 1}") + def on_elevator_move( + self, elevator: ProxyElevator, from_position: float, to_position: float, direction: str, status: str + ) -> None: + """ + 电梯移动时的回调 + 可以在这里记录电梯移动信息,用于调试或性能分析 + """ + # 取消注释以显示电梯移动信息 + # print(f"🚀 电梯 E{elevator.id} 移动: {from_position:.1f} -> {to_position:.1f} ({direction}, {status})") + pass + if __name__ == "__main__": algorithm = ElevatorBusController(debug=True) diff --git a/elevator_saga/core/models.py b/elevator_saga/core/models.py index c9e2715..176e9be 100644 --- a/elevator_saga/core/models.py +++ b/elevator_saga/core/models.py @@ -8,7 +8,7 @@ import uuid from dataclasses import asdict, dataclass, field from datetime import datetime from enum import Enum -from typing import Any, Callable, Dict, List, Optional, Tuple, Type, TypeVar, Union +from typing import Any, Dict, List, Optional, Tuple, Type, TypeVar, Union # 类型变量 T = TypeVar("T", bound="SerializableModel") @@ -55,6 +55,7 @@ class EventType(Enum): IDLE = "idle" PASSENGER_BOARD = "passenger_board" PASSENGER_ALIGHT = "passenger_alight" + ELEVATOR_MOVE = "elevator_move" # 电梯移动事件 class SerializableModel: @@ -113,7 +114,7 @@ class Position(SerializableModel): @property def current_floor_float(self) -> float: - return self.current_floor + self.floor_up_position / 10 + return round(self.current_floor + self.floor_up_position / 10, 1) def floor_up_position_add(self, num: int) -> int: self.floor_up_position += num diff --git a/elevator_saga/server/simulator.py b/elevator_saga/server/simulator.py index 65ade97..b22623c 100644 --- a/elevator_saga/server/simulator.py +++ b/elevator_saga/server/simulator.py @@ -10,7 +10,7 @@ import threading from dataclasses import dataclass from enum import Enum from pathlib import Path -from typing import Any, Dict, List, Optional, cast +from typing import Any, Dict, List, cast from flask import Flask, Response, request @@ -274,7 +274,7 @@ class ElevatorSimulation: # 2. Move elevators self._move_elevators() - # 3. Process elevator stops and passenger boarding/alighting + # 3. Process elevator stops and passenger alighting self._process_elevator_stops() # Return events generated this tick @@ -310,7 +310,6 @@ class ElevatorSimulation: def _update_elevator_status(self) -> None: """更新电梯运行状态""" for elevator in self.elevators: - current_floor = elevator.position.current_floor target_floor = elevator.target_floor old_status = elevator.run_status.value # 没有移动方向,说明电梯已经到达目标楼层 @@ -378,6 +377,7 @@ class ElevatorSimulation: # 根据状态和方向调整移动距离 elevator.last_tick_direction = elevator.target_floor_direction + old_position = elevator.position.current_floor_float if elevator.target_floor_direction == Direction.UP: new_floor = elevator.position.floor_up_position_add(movement_speed) elif elevator.target_floor_direction == Direction.DOWN: @@ -386,6 +386,19 @@ class ElevatorSimulation: # 之前的状态已经是到站了,清空上一次到站的方向 pass + # 发送电梯移动事件 + if elevator.target_floor_direction != Direction.STOPPED: + self._emit_event( + EventType.ELEVATOR_MOVE, + { + "elevator": elevator.id, + "from_position": old_position, + "to_position": elevator.position.current_floor_float, + "direction": elevator.target_floor_direction.value, + "status": elevator.run_status.value, + }, + ) + # 移动后检测是否即将到站,从匀速状态切换到减速 if elevator.run_status == ElevatorStatus.CONSTANT_SPEED: # 检查是否需要开始减速,这里加速减速设置路程为1,匀速路程为2,这样能够保证不会匀速恰好到达,必须加减速 @@ -465,7 +478,6 @@ class ElevatorSimulation: [SERVER-DEBUG] 电梯 E0 被设定为前往 F1 说明电梯处于stop状态,这个tick直接采用下一个目的地运行了 """ - original_target_floor = elevator.target_floor elevator.position.target_floor = floor server_debug_log(f"电梯 E{elevator.id} 被设定为前往 F{floor}") new_target_floor_should_accel = self._should_start_deceleration(elevator)