mirror of
https://github.com/ZGCA-Forge/Elevator.git
synced 2026-02-04 05:15:19 +00:00
Update ci
This commit is contained in:
@@ -6,5 +6,5 @@ A Python implementation of the Elevator Saga game with event-driven architecture
|
||||
realistic elevator dispatch algorithm development and testing.
|
||||
"""
|
||||
|
||||
__version__ = "1.0.0"
|
||||
__version__ = "0.0.1"
|
||||
__author__ = "ZGCA Team"
|
||||
|
||||
@@ -134,12 +134,14 @@ class ElevatorAPIClient:
|
||||
def send_elevator_command(self, command: Union[GoToFloorCommand]) -> bool:
|
||||
"""发送电梯命令"""
|
||||
endpoint = self._get_elevator_endpoint(command)
|
||||
debug_log(f"Sending elevator command: {command.command_type} to elevator {command.elevator_id} To:F{command.floor}")
|
||||
debug_log(
|
||||
f"Sending elevator command: {command.command_type} to elevator {command.elevator_id} To:F{command.floor}"
|
||||
)
|
||||
|
||||
response_data = self._send_post_request(endpoint, command.parameters)
|
||||
|
||||
if response_data.get("success"):
|
||||
return response_data["success"]
|
||||
return bool(response_data["success"])
|
||||
else:
|
||||
raise RuntimeError(f"Command failed: {response_data.get('error_message')}")
|
||||
|
||||
@@ -168,7 +170,7 @@ class ElevatorAPIClient:
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(url, timeout=60) as response:
|
||||
data = json.loads(response.read().decode("utf-8"))
|
||||
data: Dict[str, Any] = json.loads(response.read().decode("utf-8"))
|
||||
# debug_log(f"GET {url} -> {response.status}")
|
||||
return data
|
||||
except urllib.error.URLError as e:
|
||||
@@ -178,7 +180,7 @@ class ElevatorAPIClient:
|
||||
"""重置模拟"""
|
||||
try:
|
||||
response_data = self._send_post_request("/api/reset", {})
|
||||
success = response_data.get("success", False)
|
||||
success = bool(response_data.get("success", False))
|
||||
if success:
|
||||
# 清空缓存,因为状态已重置
|
||||
self._cached_state = None
|
||||
@@ -190,11 +192,11 @@ class ElevatorAPIClient:
|
||||
debug_log(f"Reset failed: {e}")
|
||||
return False
|
||||
|
||||
def next_traffic_round(self, full_reset = False) -> bool:
|
||||
def next_traffic_round(self, full_reset: bool = False) -> bool:
|
||||
"""切换到下一个流量文件"""
|
||||
try:
|
||||
response_data = self._send_post_request("/api/traffic/next", {"full_reset": full_reset})
|
||||
success = response_data.get("success", False)
|
||||
success = bool(response_data.get("success", False))
|
||||
if success:
|
||||
# 清空缓存,因为流量文件已切换,状态会改变
|
||||
self._cached_state = None
|
||||
@@ -230,7 +232,7 @@ class ElevatorAPIClient:
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=600) as response:
|
||||
response_data = json.loads(response.read().decode("utf-8"))
|
||||
response_data: Dict[str, Any] = json.loads(response.read().decode("utf-8"))
|
||||
# debug_log(f"POST {url} -> {response.status}")
|
||||
return response_data
|
||||
except urllib.error.URLError as e:
|
||||
|
||||
@@ -44,7 +44,7 @@ class ElevatorController(ABC):
|
||||
self.api_client = ElevatorAPIClient(server_url)
|
||||
|
||||
@abstractmethod
|
||||
def on_init(self, elevators: List[Any], floors: List[Any]):
|
||||
def on_init(self, elevators: List[Any], floors: List[Any]) -> None:
|
||||
"""
|
||||
算法初始化方法 - 必须由子类实现
|
||||
|
||||
@@ -55,7 +55,7 @@ class ElevatorController(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def on_event_execute_start(self, tick: int, events: List[Any], elevators: List[Any], floors: List[Any]):
|
||||
def on_event_execute_start(self, tick: int, events: List[Any], elevators: List[Any], floors: List[Any]) -> None:
|
||||
"""
|
||||
事件执行前的回调 - 必须由子类实现
|
||||
|
||||
@@ -68,7 +68,7 @@ class ElevatorController(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def on_event_execute_end(self, tick: int, events: List[Any], elevators: List[Any], floors: List[Any]):
|
||||
def on_event_execute_end(self, tick: int, events: List[Any], elevators: List[Any], floors: List[Any]) -> None:
|
||||
"""
|
||||
事件执行后的回调 - 必须由子类实现
|
||||
|
||||
@@ -80,20 +80,20 @@ class ElevatorController(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
def on_start(self):
|
||||
def on_start(self) -> None:
|
||||
"""
|
||||
算法启动前的回调 - 可选实现
|
||||
"""
|
||||
print(f"启动 {self.__class__.__name__} 算法")
|
||||
|
||||
def on_stop(self):
|
||||
def on_stop(self) -> None:
|
||||
"""
|
||||
算法停止后的回调 - 可选实现
|
||||
"""
|
||||
print(f"停止 {self.__class__.__name__} 算法")
|
||||
|
||||
@abstractmethod
|
||||
def on_passenger_call(self, passenger: ProxyPassenger, floor: ProxyFloor, direction: str):
|
||||
def on_passenger_call(self, passenger: ProxyPassenger, floor: ProxyFloor, direction: str) -> None:
|
||||
"""
|
||||
乘客呼叫时的回调 - 可选实现
|
||||
|
||||
@@ -104,7 +104,7 @@ class ElevatorController(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def on_elevator_idle(self, elevator: ProxyElevator):
|
||||
def on_elevator_idle(self, elevator: ProxyElevator) -> None:
|
||||
"""
|
||||
电梯空闲时的回调 - 可选实现
|
||||
|
||||
@@ -114,7 +114,7 @@ class ElevatorController(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def on_elevator_stopped(self, elevator: ProxyElevator, floor: ProxyFloor):
|
||||
def on_elevator_stopped(self, elevator: ProxyElevator, floor: ProxyFloor) -> None:
|
||||
"""
|
||||
电梯停靠时的回调 - 可选实现
|
||||
|
||||
@@ -125,7 +125,7 @@ class ElevatorController(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def on_passenger_board(self, elevator: ProxyElevator, passenger: ProxyPassenger):
|
||||
def on_passenger_board(self, elevator: ProxyElevator, passenger: ProxyPassenger) -> None:
|
||||
"""
|
||||
乘客上梯时的回调 - 可选实现
|
||||
|
||||
@@ -136,7 +136,7 @@ class ElevatorController(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def on_passenger_alight(self, elevator: ProxyElevator, passenger: ProxyPassenger, floor: ProxyFloor):
|
||||
def on_passenger_alight(self, elevator: ProxyElevator, passenger: ProxyPassenger, floor: ProxyFloor) -> None:
|
||||
"""
|
||||
乘客下车时的回调 - 可选实现
|
||||
|
||||
@@ -148,7 +148,7 @@ class ElevatorController(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def on_elevator_passing_floor(self, elevator: ProxyElevator, floor: ProxyFloor, direction: str):
|
||||
def on_elevator_passing_floor(self, elevator: ProxyElevator, floor: ProxyFloor, direction: str) -> None:
|
||||
"""
|
||||
电梯经过楼层时的回调 - 可选实现
|
||||
|
||||
@@ -160,7 +160,7 @@ class ElevatorController(ABC):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def on_elevator_approaching(self, elevator: ProxyElevator, floor: ProxyFloor, direction: str):
|
||||
def on_elevator_approaching(self, elevator: ProxyElevator, floor: ProxyFloor, direction: str) -> None:
|
||||
"""
|
||||
电梯即将到达时的回调 - 可选实现
|
||||
|
||||
@@ -171,7 +171,7 @@ class ElevatorController(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
def _internal_init(self, elevators: List[Any], floors: List[Any]):
|
||||
def _internal_init(self, elevators: List[Any], floors: List[Any]) -> None:
|
||||
"""内部初始化方法"""
|
||||
self.elevators = elevators
|
||||
self.floors = floors
|
||||
@@ -180,7 +180,7 @@ class ElevatorController(ABC):
|
||||
# 调用用户的初始化方法
|
||||
self.on_init(elevators, floors)
|
||||
|
||||
def start(self):
|
||||
def start(self) -> None:
|
||||
"""
|
||||
启动控制器
|
||||
"""
|
||||
@@ -198,12 +198,12 @@ class ElevatorController(ABC):
|
||||
self.is_running = False
|
||||
self.on_stop()
|
||||
|
||||
def stop(self):
|
||||
def stop(self) -> None:
|
||||
"""停止控制器"""
|
||||
self.is_running = False
|
||||
print(f"停止 {self.__class__.__name__}")
|
||||
|
||||
def on_simulation_complete(self, final_state: Dict[str, Any]):
|
||||
def on_simulation_complete(self, final_state: Dict[str, Any]) -> None:
|
||||
"""
|
||||
模拟完成时的回调 - 可选实现
|
||||
|
||||
@@ -212,7 +212,7 @@ class ElevatorController(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
def _run_event_driven_simulation(self):
|
||||
def _run_event_driven_simulation(self) -> None:
|
||||
"""运行事件驱动的模拟"""
|
||||
try:
|
||||
# 获取初始状态并初始化,默认从0开始
|
||||
@@ -304,7 +304,7 @@ class ElevatorController(ABC):
|
||||
try:
|
||||
traffic_info = self.api_client.get_traffic_info()
|
||||
if traffic_info:
|
||||
self.current_traffic_max_tick = traffic_info["max_tick"]
|
||||
self.current_traffic_max_tick = int(traffic_info["max_tick"])
|
||||
debug_log(f"Updated traffic info - max_tick: {self.current_traffic_max_tick}")
|
||||
else:
|
||||
debug_log("Failed to get traffic info")
|
||||
@@ -313,7 +313,7 @@ class ElevatorController(ABC):
|
||||
debug_log(f"Error updating traffic info: {e}")
|
||||
self.current_traffic_max_tick = 0
|
||||
|
||||
def _handle_single_event(self, event: SimulationEvent):
|
||||
def _handle_single_event(self, event: SimulationEvent) -> None:
|
||||
"""处理单个事件"""
|
||||
if event.type == EventType.UP_BUTTON_PRESSED:
|
||||
floor_id = event.data["floor"]
|
||||
@@ -385,7 +385,7 @@ class ElevatorController(ABC):
|
||||
floor_proxy = ProxyFloor(floor_id, self.api_client)
|
||||
self.on_passenger_alight(elevator_proxy, passenger_proxy, floor_proxy)
|
||||
|
||||
def _reset_and_reinit(self):
|
||||
def _reset_and_reinit(self) -> None:
|
||||
"""重置并重新初始化"""
|
||||
try:
|
||||
# 重置服务器状态
|
||||
|
||||
@@ -22,6 +22,8 @@ class ProxyFloor(FloorState):
|
||||
"""获取 FloorState 实例"""
|
||||
state = self._api_client.get_state()
|
||||
floor_data = next((f for f in state.floors if f.floor == self._floor_id), None)
|
||||
if floor_data is None:
|
||||
raise ValueError(f"Floor {self._floor_id} not found in state")
|
||||
return floor_data
|
||||
|
||||
def __getattribute__(self, name: str) -> Any:
|
||||
@@ -66,6 +68,8 @@ class ProxyElevator(ElevatorState):
|
||||
# 获取当前状态
|
||||
state = self._api_client.get_state()
|
||||
elevator_data = next((e for e in state.elevators if e.id == self._elevator_id), None)
|
||||
if elevator_data is None:
|
||||
raise ValueError(f"Elevator {self._elevator_id} not found in state")
|
||||
return elevator_data
|
||||
|
||||
def __getattribute__(self, name: str) -> Any:
|
||||
@@ -113,6 +117,8 @@ class ProxyPassenger(PassengerInfo):
|
||||
"""获取 PassengerInfo 实例"""
|
||||
state = self._api_client.get_state()
|
||||
passenger_data = state.passengers.get(self._passenger_id)
|
||||
if passenger_data is None:
|
||||
raise ValueError(f"Passenger {self._passenger_id} not found in state")
|
||||
return passenger_data
|
||||
|
||||
def __getattribute__(self, name: str) -> Any:
|
||||
|
||||
0
elevator_saga/client_examples/__init__.py
Normal file
0
elevator_saga/client_examples/__init__.py
Normal file
77
elevator_saga/client_examples/bus_example.py
Normal file
77
elevator_saga/client_examples/bus_example.py
Normal file
@@ -0,0 +1,77 @@
|
||||
#!/usr/bin/env python3
|
||||
from typing import List
|
||||
|
||||
from elevator_saga.client.base_controller import ElevatorController
|
||||
from elevator_saga.client.proxy_models import ProxyElevator, ProxyFloor, ProxyPassenger
|
||||
from elevator_saga.core.models import Direction, SimulationEvent
|
||||
|
||||
|
||||
class ElevatorBusExampleController(ElevatorController):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("http://127.0.0.1:8000", True)
|
||||
self.all_passengers: List[ProxyPassenger] = []
|
||||
self.max_floor = 0
|
||||
|
||||
def on_init(self, elevators: List[ProxyElevator], floors: List[ProxyFloor]) -> None:
|
||||
self.max_floor = floors[-1].floor
|
||||
self.floors = floors
|
||||
for i, elevator in enumerate(elevators):
|
||||
# 计算目标楼层 - 均匀分布在不同楼层
|
||||
target_floor = (i * (len(floors) - 1)) // len(elevators)
|
||||
# 立刻移动到目标位置并开始循环
|
||||
elevator.go_to_floor(target_floor, immediate=True)
|
||||
pass
|
||||
|
||||
def on_event_execute_start(
|
||||
self, tick: int, events: List[SimulationEvent], elevators: List[ProxyElevator], floors: List[ProxyFloor]
|
||||
) -> None:
|
||||
print(f"Tick {tick}: 即将处理 {len(events)} 个事件 {[e.type.value for e in events]}")
|
||||
for i in elevators:
|
||||
print(
|
||||
f"\t{i.id}[{i.target_floor_direction.value},{i.current_floor_float}/{i.target_floor}]"
|
||||
+ "👦" * len(i.passengers),
|
||||
end="",
|
||||
)
|
||||
print()
|
||||
|
||||
def on_event_execute_end(
|
||||
self, tick: int, events: List[SimulationEvent], elevators: List[ProxyElevator], floors: List[ProxyFloor]
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
def on_passenger_call(self, passenger: ProxyPassenger, floor: ProxyFloor, direction: str) -> None:
|
||||
self.all_passengers.append(passenger)
|
||||
pass
|
||||
|
||||
def on_elevator_idle(self, elevator: ProxyElevator) -> None:
|
||||
elevator.go_to_floor(1)
|
||||
|
||||
def on_elevator_stopped(self, elevator: ProxyElevator, floor: ProxyFloor) -> None:
|
||||
print(f"🛑 电梯 E{elevator.id} 停靠在 F{floor.floor}")
|
||||
# BUS调度算法,电梯到达顶层后,立即下降一层
|
||||
if elevator.last_tick_direction == Direction.UP and elevator.current_floor == self.max_floor:
|
||||
elevator.go_to_floor(elevator.current_floor - 1)
|
||||
# 电梯到达底层后,立即上升一层
|
||||
elif elevator.last_tick_direction == Direction.DOWN and elevator.current_floor == 0:
|
||||
elevator.go_to_floor(elevator.current_floor + 1)
|
||||
elif elevator.last_tick_direction == Direction.UP:
|
||||
elevator.go_to_floor(elevator.current_floor + 1)
|
||||
elif elevator.last_tick_direction == Direction.DOWN:
|
||||
elevator.go_to_floor(elevator.current_floor - 1)
|
||||
|
||||
def on_passenger_board(self, elevator: ProxyElevator, passenger: ProxyPassenger) -> None:
|
||||
pass
|
||||
|
||||
def on_passenger_alight(self, elevator: ProxyElevator, passenger: ProxyPassenger, floor: ProxyFloor) -> None:
|
||||
pass
|
||||
|
||||
def on_elevator_passing_floor(self, elevator: ProxyElevator, floor: ProxyFloor, direction: str) -> None:
|
||||
pass
|
||||
|
||||
def on_elevator_approaching(self, elevator: ProxyElevator, floor: ProxyFloor, direction: str) -> None:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
algorithm = ElevatorBusExampleController()
|
||||
algorithm.start()
|
||||
145
elevator_saga/client_examples/simple_example.py
Normal file
145
elevator_saga/client_examples/simple_example.py
Normal file
@@ -0,0 +1,145 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
公交车式电梯调度算法示例
|
||||
电梯像公交车一样运营,按固定路线循环停靠每一层
|
||||
"""
|
||||
from typing import Dict, List
|
||||
|
||||
from elevator_saga.client.base_controller import ElevatorController
|
||||
from elevator_saga.client.proxy_models import ProxyElevator, ProxyFloor, ProxyPassenger
|
||||
from elevator_saga.core.models import Direction, SimulationEvent
|
||||
|
||||
|
||||
class ElevatorBusController(ElevatorController):
|
||||
"""
|
||||
公交车式电梯调度算法
|
||||
电梯像公交车一样按固定路线循环运行,在每层都停
|
||||
"""
|
||||
|
||||
def __init__(self, server_url: str = "http://127.0.0.1:8000", debug: bool = False):
|
||||
"""初始化控制器"""
|
||||
super().__init__(server_url, debug)
|
||||
self.elevator_directions: Dict[int, str] = {} # 记录每个电梯的当前方向
|
||||
self.max_floor = 0 # 最大楼层数
|
||||
|
||||
def on_init(self, elevators: List[ProxyElevator], floors: List[ProxyFloor]) -> None:
|
||||
"""初始化公交车式电梯算法"""
|
||||
print("🚌 公交车式电梯算法初始化")
|
||||
print(f" 管理 {len(elevators)} 部电梯")
|
||||
print(f" 服务 {len(floors)} 层楼")
|
||||
# 获取最大楼层数
|
||||
self.max_floor = len(floors) - 1
|
||||
# 初始化每个电梯的方向 - 开始都向上
|
||||
for elevator in elevators:
|
||||
self.elevator_directions[elevator.id] = "up"
|
||||
# 简单的初始分布 - 均匀分散到不同楼层
|
||||
for i, elevator in enumerate(elevators):
|
||||
# 计算目标楼层 - 均匀分布在不同楼层
|
||||
target_floor = (i * (len(floors) - 1)) // len(elevators)
|
||||
# 立刻移动到目标位置并开始循环
|
||||
elevator.go_to_floor(target_floor, immediate=True)
|
||||
|
||||
def on_event_execute_start(
|
||||
self, tick: int, events: List[SimulationEvent], elevators: List[ProxyElevator], floors: List[ProxyFloor]
|
||||
) -> None:
|
||||
"""事件执行前的回调"""
|
||||
print(f"Tick {tick}: 即将处理 {len(events)} 个事件 {[e.type.value for e in events]}")
|
||||
for i in elevators:
|
||||
print(
|
||||
f"\t{i.id}[{i.target_floor_direction.value},{i.current_floor_float}/{i.target_floor}]"
|
||||
+ "👦" * len(i.passengers),
|
||||
end="",
|
||||
)
|
||||
print()
|
||||
|
||||
def on_event_execute_end(
|
||||
self, tick: int, events: List[SimulationEvent], elevators: List[ProxyElevator], floors: List[ProxyFloor]
|
||||
) -> None:
|
||||
"""事件执行后的回调"""
|
||||
# print(f"✅ Tick {tick}: 已处理 {len(events)} 个事件")
|
||||
pass
|
||||
|
||||
def on_passenger_call(self, passenger: ProxyPassenger, floor: ProxyFloor, direction: str) -> None:
|
||||
"""
|
||||
乘客呼叫时的回调
|
||||
公交车模式下,电梯已经在循环运行,无需特别响应呼叫
|
||||
"""
|
||||
print(f"乘客 {passenger.id} F{floor.floor} 请求 {passenger.origin} -> {passenger.destination} ({direction})")
|
||||
|
||||
def on_elevator_idle(self, elevator: ProxyElevator) -> None:
|
||||
"""
|
||||
电梯空闲时的回调
|
||||
让空闲的电梯继续执行公交车循环路线,每次移动一层楼
|
||||
"""
|
||||
print(f"🛑 电梯 E{elevator.id} 在 F{elevator.current_floor} 层空闲")
|
||||
# 设置指示器让乘客知道电梯的行进方向
|
||||
if self.elevator_directions[elevator.id] == "down" and elevator.current_floor != 0:
|
||||
elevator.go_to_floor(elevator.current_floor - 1, immediate=True)
|
||||
# elevator.set_up_indicator(True)
|
||||
elevator.go_to_floor(1)
|
||||
# current_direction = self.elevator_directions[elevator.id]
|
||||
# if current_direction == "up":
|
||||
# elevator.set_up_indicator(True)
|
||||
# elevator.set_down_indicator(False)
|
||||
# else:
|
||||
# elevator.set_up_indicator(False)
|
||||
# elevator.set_down_indicator(True)
|
||||
|
||||
def on_elevator_stopped(self, elevator: ProxyElevator, floor: ProxyFloor) -> None:
|
||||
"""
|
||||
电梯停靠时的回调
|
||||
公交车模式下,在每一层都停下,然后继续下一站
|
||||
需要注意的是,stopped会比idle先触发
|
||||
"""
|
||||
print(f"🛑 电梯 E{elevator.id} 停靠在 F{floor.floor}")
|
||||
if self.elevator_directions[elevator.id] == "up" and elevator.current_floor == self.max_floor:
|
||||
elevator.go_to_floor(elevator.current_floor - 1, immediate=True)
|
||||
self.elevator_directions[elevator.id] = "down"
|
||||
elif self.elevator_directions[elevator.id] == "down" and elevator.current_floor == 0:
|
||||
elevator.go_to_floor(elevator.current_floor + 1, immediate=True)
|
||||
self.elevator_directions[elevator.id] = "up"
|
||||
elif self.elevator_directions[elevator.id] == "up":
|
||||
# if elevator.id == 0:
|
||||
# raise ValueError("这里故意要求0号电梯不可能触发非两端停止,通过on_elevator_approaching实现")
|
||||
elevator.go_to_floor(elevator.current_floor + 1, immediate=True)
|
||||
# 这里故意少写下降的情况,用于了解stopped会先于idle触发
|
||||
# elif self.elevator_directions[elevator.id] == "down":
|
||||
# elevator.go_to_floor(elevator.current_floor - 1, immediate=True)
|
||||
# self.elevator_directions[elevator.id] = "down"
|
||||
|
||||
def on_passenger_board(self, elevator: ProxyElevator, passenger: ProxyPassenger) -> None:
|
||||
"""
|
||||
乘客上梯时的回调
|
||||
打印乘客上梯信息
|
||||
"""
|
||||
print(f" 乘客{passenger.id} E{elevator.id}⬆️ F{elevator.current_floor} -> F{passenger.destination}")
|
||||
|
||||
def on_passenger_alight(self, elevator: ProxyElevator, passenger: ProxyPassenger, floor: ProxyFloor) -> None:
|
||||
"""
|
||||
乘客下车时的回调
|
||||
打印乘客下车信息
|
||||
"""
|
||||
print(f" 乘客{passenger.id} E{elevator.id}⬇️ F{floor.floor}")
|
||||
|
||||
def on_elevator_passing_floor(self, elevator: ProxyElevator, floor: ProxyFloor, direction: str) -> None:
|
||||
"""
|
||||
电梯经过楼层时的回调
|
||||
打印经过楼层的信息
|
||||
"""
|
||||
print(f"🔄 电梯 E{elevator.id} 经过 F{floor.floor} (方向: {direction})")
|
||||
|
||||
def on_elevator_approaching(self, elevator: ProxyElevator, floor: ProxyFloor, direction: str) -> None:
|
||||
"""
|
||||
电梯即将到达时的回调 (START_DOWN事件)
|
||||
电梯开始减速,即将到达目标楼层
|
||||
"""
|
||||
print(f"🎯 电梯 E{elevator.id} 即将到达 F{floor.floor} (方向: {direction})")
|
||||
if elevator.target_floor == floor.floor and elevator.target_floor_direction == Direction.UP: # 电梯的目标楼层就是即将停靠的楼层
|
||||
if elevator.id == 0: # 这里为了测试,让0号电梯往上一层就新加一层,上行永远不会开门
|
||||
elevator.go_to_floor(elevator.target_floor + 1, immediate=True)
|
||||
print(f" 不让0号电梯上行停站,设定新目标楼层 {elevator.target_floor + 1}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
algorithm = ElevatorBusController(debug=True)
|
||||
algorithm.start()
|
||||
@@ -85,7 +85,6 @@ class SerializableModel:
|
||||
setattr(instance, k, v.__class__(value))
|
||||
return instance
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_json(cls: Type[T], json_str: str) -> T:
|
||||
"""从JSON字符串创建实例"""
|
||||
@@ -113,10 +112,10 @@ class Position(SerializableModel):
|
||||
floor_up_position: int = 0
|
||||
|
||||
@property
|
||||
def current_floor_float(self):
|
||||
def current_floor_float(self) -> float:
|
||||
return self.current_floor + self.floor_up_position / 10
|
||||
|
||||
def floor_up_position_add(self, num: int):
|
||||
def floor_up_position_add(self, num: int) -> int:
|
||||
self.floor_up_position += num
|
||||
|
||||
# 处理向上楼层跨越
|
||||
@@ -139,7 +138,7 @@ class ElevatorIndicators(SerializableModel):
|
||||
up: bool = False
|
||||
down: bool = False
|
||||
|
||||
def set_direction(self, direction: Direction):
|
||||
def set_direction(self, direction: Direction) -> None:
|
||||
"""根据方向设置指示灯"""
|
||||
if direction == Direction.UP:
|
||||
self.up = True
|
||||
@@ -202,13 +201,13 @@ class ElevatorState(SerializableModel):
|
||||
id: int
|
||||
position: Position
|
||||
next_target_floor: Optional[int] = None
|
||||
passengers: List[int] = field(default_factory=list) # type: ignore[reportUnknownVariableType] 乘客ID列表
|
||||
passengers: List[int] = field(default_factory=list) # 乘客ID列表
|
||||
max_capacity: int = 10
|
||||
speed_pre_tick: float = 0.5
|
||||
run_status: ElevatorStatus = ElevatorStatus.STOPPED
|
||||
last_tick_direction: Direction = Direction.STOPPED
|
||||
indicators: ElevatorIndicators = field(default_factory=ElevatorIndicators)
|
||||
passenger_destinations: Dict[int, int] = field(default_factory=dict) # type: ignore[reportUnknownVariableType] 乘客ID -> 目的地楼层映射
|
||||
passenger_destinations: Dict[int, int] = field(default_factory=dict) # 乘客ID -> 目的地楼层映射
|
||||
energy_consumed: float = 0.0
|
||||
last_update_tick: int = 0
|
||||
|
||||
@@ -223,7 +222,7 @@ class ElevatorState(SerializableModel):
|
||||
def current_floor_float(self) -> float:
|
||||
"""当前楼层"""
|
||||
if isinstance(self.position, dict):
|
||||
self.position = Position.from_dict(self.position)
|
||||
self.position = Position.from_dict(self.position) # type: ignore[arg-type]
|
||||
return self.position.current_floor_float
|
||||
|
||||
@property
|
||||
@@ -269,7 +268,7 @@ class ElevatorState(SerializableModel):
|
||||
"""按下的楼层(基于当前乘客的目的地动态计算)"""
|
||||
return sorted(list(set(self.passenger_destinations.values())))
|
||||
|
||||
def clear_destinations(self):
|
||||
def clear_destinations(self) -> None:
|
||||
"""清空目标队列"""
|
||||
self.next_target_floor = None
|
||||
|
||||
@@ -279,8 +278,8 @@ class FloorState(SerializableModel):
|
||||
"""楼层状态"""
|
||||
|
||||
floor: int
|
||||
up_queue: List[int] = field(default_factory=list) # type: ignore[reportUnknownVariableType] 等待上行的乘客ID
|
||||
down_queue: List[int] = field(default_factory=list) # type: ignore[reportUnknownVariableType] 等待下行的乘客ID
|
||||
up_queue: List[int] = field(default_factory=list) # 等待上行的乘客ID
|
||||
down_queue: List[int] = field(default_factory=list) # 等待下行的乘客ID
|
||||
|
||||
@property
|
||||
def has_waiting_passengers(self) -> bool:
|
||||
@@ -292,7 +291,7 @@ class FloorState(SerializableModel):
|
||||
"""总等待人数"""
|
||||
return len(self.up_queue) + len(self.down_queue)
|
||||
|
||||
def add_waiting_passenger(self, passenger_id: int, direction: Direction):
|
||||
def add_waiting_passenger(self, passenger_id: int, direction: Direction) -> None:
|
||||
"""添加等待乘客"""
|
||||
if direction == Direction.UP:
|
||||
if passenger_id not in self.up_queue:
|
||||
@@ -321,7 +320,7 @@ class SimulationEvent(SerializableModel):
|
||||
data: Dict[str, Any]
|
||||
timestamp: Optional[str] = None
|
||||
|
||||
def __post_init__(self):
|
||||
def __post_init__(self) -> None:
|
||||
if self.timestamp is None:
|
||||
self.timestamp = datetime.now().isoformat()
|
||||
|
||||
@@ -360,9 +359,9 @@ class SimulationState(SerializableModel):
|
||||
tick: int
|
||||
elevators: List[ElevatorState]
|
||||
floors: List[FloorState]
|
||||
passengers: Dict[int, PassengerInfo] = field(default_factory=dict) # type: ignore[reportUnknownVariableType]
|
||||
passengers: Dict[int, PassengerInfo] = field(default_factory=dict)
|
||||
metrics: PerformanceMetrics = field(default_factory=PerformanceMetrics)
|
||||
events: List[SimulationEvent] = field(default_factory=list) # type: ignore[reportUnknownVariableType]
|
||||
events: List[SimulationEvent] = field(default_factory=list)
|
||||
|
||||
def get_elevator_by_id(self, elevator_id: int) -> Optional[ElevatorState]:
|
||||
"""根据ID获取电梯"""
|
||||
@@ -382,7 +381,7 @@ class SimulationState(SerializableModel):
|
||||
"""根据状态获取乘客"""
|
||||
return [p for p in self.passengers.values() if p.status == status]
|
||||
|
||||
def add_event(self, event_type: EventType, data: Dict[str, Any]):
|
||||
def add_event(self, event_type: EventType, data: Dict[str, Any]) -> None:
|
||||
"""添加事件"""
|
||||
event = SimulationEvent(tick=self.tick, type=event_type, data=data)
|
||||
self.events.append(event)
|
||||
@@ -422,7 +421,7 @@ class StepResponse(SerializableModel):
|
||||
|
||||
success: bool
|
||||
tick: int
|
||||
events: List[SimulationEvent] = field(default_factory=list) # type: ignore[reportUnknownVariableType]
|
||||
events: List[SimulationEvent] = field(default_factory=list)
|
||||
request_id: Optional[str] = None
|
||||
error_message: Optional[str] = None
|
||||
timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
|
||||
@@ -443,7 +442,7 @@ class ElevatorCommand(SerializableModel):
|
||||
|
||||
elevator_id: int
|
||||
command_type: str # "go_to_floor", "stop"
|
||||
parameters: Dict[str, Any] = field(default_factory=dict) # type: ignore[reportUnknownVariableType]
|
||||
parameters: Dict[str, Any] = field(default_factory=dict)
|
||||
request_id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
||||
timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
|
||||
|
||||
@@ -494,7 +493,7 @@ class TrafficPattern(SerializableModel):
|
||||
entries: List[TrafficEntry] = field(default_factory=list)
|
||||
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def add_entry(self, entry: TrafficEntry):
|
||||
def add_entry(self, entry: TrafficEntry) -> None:
|
||||
"""添加流量条目"""
|
||||
self.entries.append(entry)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from typing import Dict, List
|
||||
|
||||
from elevator_saga.client.base_controller import ElevatorController
|
||||
from elevator_saga.client.proxy_models import ProxyElevator, ProxyFloor, ProxyPassenger
|
||||
from elevator_saga.core.models import SimulationEvent, Direction
|
||||
from elevator_saga.core.models import Direction, SimulationEvent
|
||||
|
||||
|
||||
class ElevatorBusController(ElevatorController):
|
||||
@@ -45,7 +45,11 @@ class ElevatorBusController(ElevatorController):
|
||||
"""事件执行前的回调"""
|
||||
print(f"Tick {tick}: 即将处理 {len(events)} 个事件 {[e.type.value for e in events]}")
|
||||
for i in elevators:
|
||||
print(f"\t{i.id}[{i.target_floor_direction.value},{i.current_floor_float}/{i.target_floor}]" + "👦" * len(i.passengers), end="")
|
||||
print(
|
||||
f"\t{i.id}[{i.target_floor_direction.value},{i.current_floor_float}/{i.target_floor}]"
|
||||
+ "👦" * len(i.passengers),
|
||||
end="",
|
||||
)
|
||||
print()
|
||||
|
||||
def on_event_execute_end(
|
||||
@@ -54,7 +58,7 @@ class ElevatorBusController(ElevatorController):
|
||||
"""事件执行后的回调"""
|
||||
pass
|
||||
|
||||
def on_passenger_call(self, passenger:ProxyPassenger, floor: ProxyFloor, direction: str) -> None:
|
||||
def on_passenger_call(self, passenger: ProxyPassenger, floor: ProxyFloor, direction: str) -> None:
|
||||
"""
|
||||
乘客呼叫时的回调
|
||||
公交车模式下,电梯已经在循环运行,无需特别响应呼叫
|
||||
@@ -107,9 +111,7 @@ class ElevatorBusController(ElevatorController):
|
||||
乘客上梯时的回调
|
||||
打印乘客上梯信息
|
||||
"""
|
||||
print(
|
||||
f" 乘客{passenger.id} E{elevator.id}⬆️ F{elevator.current_floor} -> F{passenger.destination}"
|
||||
)
|
||||
print(f" 乘客{passenger.id} E{elevator.id}⬆️ F{elevator.current_floor} -> F{passenger.destination}")
|
||||
|
||||
def on_passenger_alight(self, elevator: ProxyElevator, passenger: ProxyPassenger, floor: ProxyFloor) -> None:
|
||||
"""
|
||||
@@ -136,6 +138,7 @@ class ElevatorBusController(ElevatorController):
|
||||
elevator.go_to_floor(elevator.target_floor + 1, immediate=True)
|
||||
print(f" 不让0号电梯上行停站,设定新目标楼层 {elevator.target_floor + 1}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
algorithm = ElevatorBusController(debug=True)
|
||||
algorithm.start()
|
||||
|
||||
@@ -33,13 +33,13 @@ from elevator_saga.core.models import (
|
||||
_SERVER_DEBUG_MODE = False
|
||||
|
||||
|
||||
def set_server_debug_mode(enabled: bool):
|
||||
def set_server_debug_mode(enabled: bool) -> None:
|
||||
"""Enable or disable server debug logging"""
|
||||
global _SERVER_DEBUG_MODE
|
||||
globals()["_SERVER_DEBUG_MODE"] = enabled
|
||||
|
||||
|
||||
def server_debug_log(message: str):
|
||||
def server_debug_log(message: str) -> None:
|
||||
"""Print server debug message if debug mode is enabled"""
|
||||
if _SERVER_DEBUG_MODE:
|
||||
print(f"[SERVER-DEBUG] {message}", flush=True)
|
||||
@@ -360,9 +360,7 @@ class ElevatorSimulation:
|
||||
destination=traffic_entry.destination,
|
||||
arrive_tick=self.tick,
|
||||
)
|
||||
assert (
|
||||
traffic_entry.origin != traffic_entry.destination
|
||||
), f"乘客{passenger.id}目的地和起始地{traffic_entry.origin}重复"
|
||||
assert traffic_entry.origin != traffic_entry.destination, f"乘客{passenger.id}目的地和起始地{traffic_entry.origin}重复"
|
||||
self.passengers[passenger.id] = passenger
|
||||
server_debug_log(f"乘客 {passenger.id:4}: 创建 | {passenger}")
|
||||
if passenger.destination > passenger.origin:
|
||||
@@ -434,7 +432,9 @@ class ElevatorSimulation:
|
||||
if target_floor == new_floor and elevator.position.floor_up_position == 0:
|
||||
elevator.run_status = ElevatorStatus.STOPPED
|
||||
# 刚进入Stopped状态,可以通过last_direction识别
|
||||
self._emit_event(EventType.STOPPED_AT_FLOOR, {"elevator": elevator.id, "floor": new_floor, "reason": "move_reached"})
|
||||
self._emit_event(
|
||||
EventType.STOPPED_AT_FLOOR, {"elevator": elevator.id, "floor": new_floor, "reason": "move_reached"}
|
||||
)
|
||||
# elevator.energy_consumed += abs(direction * elevator.speed_pre_tick) * 0.5
|
||||
|
||||
def _process_elevator_stops(self) -> None:
|
||||
@@ -471,7 +471,7 @@ class ElevatorSimulation:
|
||||
self._set_elevator_target_floor(elevator, elevator.next_target_floor)
|
||||
elevator.next_target_floor = None
|
||||
|
||||
def _set_elevator_target_floor(self, elevator: ElevatorState, floor: int):
|
||||
def _set_elevator_target_floor(self, elevator: ElevatorState, floor: int) -> None:
|
||||
"""
|
||||
同一个tick内提示
|
||||
[SERVER-DEBUG] 电梯 E0 下一目的地设定为 F1
|
||||
@@ -492,9 +492,7 @@ class ElevatorSimulation:
|
||||
server_debug_log(f"电梯 E{elevator.id} 被设定为减速")
|
||||
if elevator.current_floor != floor or elevator.position.floor_up_position != 0:
|
||||
old_status = elevator.run_status.value
|
||||
server_debug_log(
|
||||
f"电梯{elevator.id} 状态:{old_status}->{elevator.run_status.value}"
|
||||
)
|
||||
server_debug_log(f"电梯{elevator.id} 状态:{old_status}->{elevator.run_status.value}")
|
||||
|
||||
def _calculate_distance_to_target(self, elevator: ElevatorState) -> float:
|
||||
"""计算到目标楼层的距离(以floor_up_position为单位)"""
|
||||
|
||||
@@ -455,4 +455,4 @@
|
||||
"tick": 196
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,4 +179,4 @@
|
||||
"tick": 74
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import math
|
||||
import os.path
|
||||
import random
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any, Callable, Dict, List, Optional
|
||||
|
||||
# 建筑规模配置
|
||||
BUILDING_SCALES = {
|
||||
@@ -294,9 +294,7 @@ def generate_fire_evacuation_traffic(
|
||||
# 在10个tick内陆续到达,模拟疏散的紧急性
|
||||
arrival_tick = alarm_tick + random.randint(0, min(10, duration - alarm_tick - 1))
|
||||
if arrival_tick < duration:
|
||||
traffic.append(
|
||||
{"id": passenger_id, "origin": floor, "destination": 0, "tick": arrival_tick} # 疏散到大厅
|
||||
)
|
||||
traffic.append({"id": passenger_id, "origin": floor, "destination": 0, "tick": arrival_tick}) # 疏散到大厅
|
||||
passenger_id += 1
|
||||
|
||||
return limit_traffic_count(traffic, max_people)
|
||||
@@ -738,12 +736,12 @@ def determine_building_scale(floors: int, elevators: int) -> str:
|
||||
return "large"
|
||||
|
||||
|
||||
def generate_traffic_file(scenario: str, output_file: str, scale: Optional[str] = None, **kwargs) -> int:
|
||||
def generate_traffic_file(scenario: str, output_file: str, scale: Optional[str] = None, **kwargs: Any) -> int:
|
||||
"""生成单个流量文件,支持规模化配置"""
|
||||
if scenario not in TRAFFIC_SCENARIOS:
|
||||
raise ValueError(f"Unknown scenario: {scenario}. Available: {list(TRAFFIC_SCENARIOS.keys())}")
|
||||
|
||||
config = TRAFFIC_SCENARIOS[scenario]
|
||||
config: Dict[str, Any] = TRAFFIC_SCENARIOS[scenario]
|
||||
|
||||
# 确定建筑规模
|
||||
if scale is None:
|
||||
@@ -766,6 +764,7 @@ def generate_traffic_file(scenario: str, output_file: str, scale: Optional[str]
|
||||
scale_params = config["scales"].get(scale, {})
|
||||
|
||||
# 合并参数:kwargs > scale_params > building_scale_defaults
|
||||
assert scale is not None # scale should be determined by this point
|
||||
building_scale = BUILDING_SCALES[scale]
|
||||
params = {}
|
||||
|
||||
@@ -786,9 +785,10 @@ def generate_traffic_file(scenario: str, output_file: str, scale: Optional[str]
|
||||
# 生成流量数据 - 只传递生成器函数需要的参数
|
||||
import inspect
|
||||
|
||||
generator_signature = inspect.signature(config["generator"])
|
||||
generator_func: Callable[..., List[Dict[str, Any]]] = config["generator"]
|
||||
generator_signature = inspect.signature(generator_func)
|
||||
generator_params = {k: v for k, v in params.items() if k in generator_signature.parameters}
|
||||
traffic_data = config["generator"](**generator_params)
|
||||
traffic_data = generator_func(**generator_params)
|
||||
|
||||
# 准备building配置
|
||||
building_config = {
|
||||
@@ -819,7 +819,7 @@ def generate_scaled_traffic_files(
|
||||
seed: int = 42,
|
||||
generate_all_scales: bool = False,
|
||||
custom_building: Optional[Dict[str, Any]] = None,
|
||||
):
|
||||
) -> None:
|
||||
"""生成按规模分类的流量文件"""
|
||||
output_path = Path(output_dir)
|
||||
output_path.mkdir(exist_ok=True)
|
||||
@@ -848,7 +848,7 @@ def generate_scaled_traffic_files(
|
||||
|
||||
def _generate_files_for_scale(
|
||||
output_path: Path, scale: str, seed: int, custom_building: Optional[Dict[str, Any]] = None
|
||||
):
|
||||
) -> None:
|
||||
"""为指定规模生成所有适合的场景文件"""
|
||||
building_config = BUILDING_SCALES[scale]
|
||||
total_passengers = 0
|
||||
@@ -868,9 +868,10 @@ def _generate_files_for_scale(
|
||||
print(f"\nGenerating {scale} scale traffic files:")
|
||||
print(f"Building: {floors} floors, {elevators} elevators, capacity {elevator_capacity}")
|
||||
|
||||
for scenario_name, config in TRAFFIC_SCENARIOS.items():
|
||||
for scenario_name, scenario_config in TRAFFIC_SCENARIOS.items():
|
||||
# 检查场景是否适合该规模
|
||||
if scale not in config["suitable_scales"]:
|
||||
config_dict: Dict[str, Any] = scenario_config
|
||||
if scale not in config_dict["suitable_scales"]:
|
||||
continue
|
||||
|
||||
filename = f"{scenario_name}.json"
|
||||
@@ -905,7 +906,7 @@ def generate_all_traffic_files(
|
||||
elevators: int = 2,
|
||||
elevator_capacity: int = 8,
|
||||
seed: int = 42,
|
||||
):
|
||||
) -> None:
|
||||
"""生成所有场景的流量文件 - 保持向后兼容"""
|
||||
scale = determine_building_scale(floors, elevators)
|
||||
custom_building = {"floors": floors, "elevators": elevators, "capacity": elevator_capacity}
|
||||
@@ -913,7 +914,7 @@ def generate_all_traffic_files(
|
||||
generate_scaled_traffic_files(output_dir=output_dir, scale=scale, seed=seed, custom_building=custom_building)
|
||||
|
||||
|
||||
def main():
|
||||
def main() -> None:
|
||||
"""主函数 - 命令行接口"""
|
||||
import argparse
|
||||
|
||||
|
||||
@@ -407,4 +407,4 @@
|
||||
"tick": 197
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,4 +371,4 @@
|
||||
"tick": 197
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,4 +161,4 @@
|
||||
"tick": 192
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,4 +491,4 @@
|
||||
"tick": 138
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -311,4 +311,4 @@
|
||||
"tick": 49
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,4 +611,4 @@
|
||||
"tick": 190
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,4 +587,4 @@
|
||||
"tick": 199
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,4 +479,4 @@
|
||||
"tick": 196
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,4 +491,4 @@
|
||||
"tick": 146
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user