mirror of
https://github.com/ZGCA-Forge/Elevator.git
synced 2025-12-17 04:51:03 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4b99daead | ||
|
|
d44ba8b6cd | ||
|
|
71e8f2a451 | ||
|
|
4b60359894 | ||
|
|
0157496e6f | ||
|
|
1031e677e1 | ||
|
|
889d554f19 |
@@ -1,5 +1,5 @@
|
|||||||
[bumpversion]
|
[bumpversion]
|
||||||
current_version = 0.0.6
|
current_version = 0.0.9
|
||||||
commit = True
|
commit = True
|
||||||
tag = True
|
tag = True
|
||||||
tag_name = v{new_version}
|
tag_name = v{new_version}
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,7 @@
|
|||||||
# ================================
|
# ================================
|
||||||
# Python-related files
|
# Python-related files
|
||||||
# ================================
|
# ================================
|
||||||
|
elevator_saga/traffic/test_cases.py
|
||||||
|
|
||||||
# Compiled Python files
|
# Compiled Python files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ The controller provides these event handlers:
|
|||||||
- ``on_passenger_alight(elevator, passenger, floor)``: Passenger alights
|
- ``on_passenger_alight(elevator, passenger, floor)``: Passenger alights
|
||||||
- ``on_elevator_passing_floor(elevator, floor, direction)``: Elevator passes floor
|
- ``on_elevator_passing_floor(elevator, floor, direction)``: Elevator passes floor
|
||||||
- ``on_elevator_approaching(elevator, floor, direction)``: Elevator about to arrive
|
- ``on_elevator_approaching(elevator, floor, direction)``: Elevator about to arrive
|
||||||
|
- ``on_elevator_move(elevator, from_position, to_position, direction, status)``: Elevator moves
|
||||||
|
|
||||||
Complete Example
|
Complete Example
|
||||||
----------------
|
----------------
|
||||||
|
|||||||
@@ -6,5 +6,5 @@ A Python implementation of the Elevator Saga game with event-driven architecture
|
|||||||
realistic elevator dispatch algorithm development and testing.
|
realistic elevator dispatch algorithm development and testing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = "0.0.6"
|
__version__ = "0.0.9"
|
||||||
__author__ = "ZGCA Team"
|
__author__ = "ZGCA Team"
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ class ElevatorController(ABC):
|
|||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
# @abstractmethod 为了兼容性暂不强制要求elevator_move必须实现
|
||||||
def on_elevator_move(
|
def on_elevator_move(
|
||||||
self, elevator: ProxyElevator, from_position: float, to_position: float, direction: str, status: str
|
self, elevator: ProxyElevator, from_position: float, to_position: float, direction: str, status: str
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
@@ -162,12 +162,13 @@ class PassengerInfo(SerializableModel):
|
|||||||
arrive_tick: int
|
arrive_tick: int
|
||||||
pickup_tick: int = 0
|
pickup_tick: int = 0
|
||||||
dropoff_tick: int = 0
|
dropoff_tick: int = 0
|
||||||
|
arrived: bool = False
|
||||||
elevator_id: Optional[int] = None
|
elevator_id: Optional[int] = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self) -> PassengerStatus:
|
def status(self) -> PassengerStatus:
|
||||||
"""乘客状态"""
|
"""乘客状态"""
|
||||||
if self.dropoff_tick > 0:
|
if self.arrived:
|
||||||
return PassengerStatus.COMPLETED
|
return PassengerStatus.COMPLETED
|
||||||
elif self.pickup_tick > 0:
|
elif self.pickup_tick > 0:
|
||||||
return PassengerStatus.IN_ELEVATOR
|
return PassengerStatus.IN_ELEVATOR
|
||||||
@@ -210,6 +211,7 @@ class ElevatorState(SerializableModel):
|
|||||||
indicators: ElevatorIndicators = field(default_factory=ElevatorIndicators)
|
indicators: ElevatorIndicators = field(default_factory=ElevatorIndicators)
|
||||||
passenger_destinations: Dict[int, int] = field(default_factory=dict) # 乘客ID -> 目的地楼层映射
|
passenger_destinations: Dict[int, int] = field(default_factory=dict) # 乘客ID -> 目的地楼层映射
|
||||||
energy_consumed: float = 0.0
|
energy_consumed: float = 0.0
|
||||||
|
energy_rate: float = 1.0 # 能耗率:每tick消耗的能量单位
|
||||||
last_update_tick: int = 0
|
last_update_tick: int = 0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -336,7 +338,7 @@ class PerformanceMetrics(SerializableModel):
|
|||||||
p95_floor_wait_time: float = 0.0
|
p95_floor_wait_time: float = 0.0
|
||||||
average_arrival_wait_time: float = 0.0
|
average_arrival_wait_time: float = 0.0
|
||||||
p95_arrival_wait_time: float = 0.0
|
p95_arrival_wait_time: float = 0.0
|
||||||
# total_energy_consumption: float = 0.0
|
total_energy_consumption: float = 0.0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def completion_rate(self) -> float:
|
def completion_rate(self) -> float:
|
||||||
@@ -345,13 +347,6 @@ class PerformanceMetrics(SerializableModel):
|
|||||||
return 0.0
|
return 0.0
|
||||||
return self.completed_passengers / self.total_passengers
|
return self.completed_passengers / self.total_passengers
|
||||||
|
|
||||||
# @property
|
|
||||||
# def energy_per_passenger(self) -> float:
|
|
||||||
# """每位乘客能耗"""
|
|
||||||
# if self.completed_passengers == 0:
|
|
||||||
# return 0.0
|
|
||||||
# return self.total_energy_consumption / self.completed_passengers
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class SimulationState(SerializableModel):
|
class SimulationState(SerializableModel):
|
||||||
|
|||||||
@@ -138,6 +138,17 @@ class ElevatorBusController(ElevatorController):
|
|||||||
elevator.go_to_floor(elevator.target_floor + 1, immediate=True)
|
elevator.go_to_floor(elevator.target_floor + 1, immediate=True)
|
||||||
print(f" 不让0号电梯上行停站,设定新目标楼层 {elevator.target_floor + 1}")
|
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__":
|
if __name__ == "__main__":
|
||||||
algorithm = ElevatorBusController(debug=True)
|
algorithm = ElevatorBusController(debug=True)
|
||||||
|
|||||||
@@ -180,6 +180,14 @@ class ElevatorSimulation:
|
|||||||
building_config["elevators"], building_config["floors"], building_config["elevator_capacity"]
|
building_config["elevators"], building_config["floors"], building_config["elevator_capacity"]
|
||||||
)
|
)
|
||||||
self.reset()
|
self.reset()
|
||||||
|
|
||||||
|
# 设置电梯能耗率
|
||||||
|
elevator_energy_rates = building_config.get("elevator_energy_rates", [1.0] * building_config["elevators"])
|
||||||
|
for i, elevator in enumerate(self.state.elevators):
|
||||||
|
if i < len(elevator_energy_rates):
|
||||||
|
elevator.energy_rate = elevator_energy_rates[i]
|
||||||
|
server_debug_log(f"电梯 E{elevator.id} 能耗率设置为: {elevator.energy_rate}")
|
||||||
|
|
||||||
self.max_duration_ticks = building_config["duration"]
|
self.max_duration_ticks = building_config["duration"]
|
||||||
traffic_data: list[Dict[str, Any]] = file_data["traffic"]
|
traffic_data: list[Dict[str, Any]] = file_data["traffic"]
|
||||||
traffic_data.sort(key=lambda t: cast(int, t["tick"]))
|
traffic_data.sort(key=lambda t: cast(int, t["tick"]))
|
||||||
@@ -380,8 +388,12 @@ class ElevatorSimulation:
|
|||||||
old_position = elevator.position.current_floor_float
|
old_position = elevator.position.current_floor_float
|
||||||
if elevator.target_floor_direction == Direction.UP:
|
if elevator.target_floor_direction == Direction.UP:
|
||||||
new_floor = elevator.position.floor_up_position_add(movement_speed)
|
new_floor = elevator.position.floor_up_position_add(movement_speed)
|
||||||
|
# 电梯移动时增加能耗,每tick增加电梯的能耗率
|
||||||
|
elevator.energy_consumed += elevator.energy_rate
|
||||||
elif elevator.target_floor_direction == Direction.DOWN:
|
elif elevator.target_floor_direction == Direction.DOWN:
|
||||||
new_floor = elevator.position.floor_up_position_add(-movement_speed)
|
new_floor = elevator.position.floor_up_position_add(-movement_speed)
|
||||||
|
# 电梯移动时增加能耗,每tick增加电梯的能耗率
|
||||||
|
elevator.energy_consumed += elevator.energy_rate
|
||||||
else:
|
else:
|
||||||
# 之前的状态已经是到站了,清空上一次到站的方向
|
# 之前的状态已经是到站了,清空上一次到站的方向
|
||||||
pass
|
pass
|
||||||
@@ -435,7 +447,6 @@ class ElevatorSimulation:
|
|||||||
self._emit_event(
|
self._emit_event(
|
||||||
EventType.STOPPED_AT_FLOOR, {"elevator": elevator.id, "floor": new_floor, "reason": "move_reached"}
|
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:
|
def _process_elevator_stops(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -457,6 +468,7 @@ class ElevatorSimulation:
|
|||||||
passenger = self.passengers[passenger_id]
|
passenger = self.passengers[passenger_id]
|
||||||
if passenger.destination == current_floor:
|
if passenger.destination == current_floor:
|
||||||
passenger.dropoff_tick = self.tick
|
passenger.dropoff_tick = self.tick
|
||||||
|
passenger.arrived = True
|
||||||
passengers_to_remove.append(passenger_id)
|
passengers_to_remove.append(passenger_id)
|
||||||
|
|
||||||
# Remove passengers who alighted
|
# Remove passengers who alighted
|
||||||
@@ -552,6 +564,10 @@ class ElevatorSimulation:
|
|||||||
completed = [p for p in self.state.passengers.values() if p.status == PassengerStatus.COMPLETED]
|
completed = [p for p in self.state.passengers.values() if p.status == PassengerStatus.COMPLETED]
|
||||||
|
|
||||||
total_passengers = len(self.state.passengers)
|
total_passengers = len(self.state.passengers)
|
||||||
|
|
||||||
|
# 计算总能耗
|
||||||
|
total_energy = sum(elevator.energy_consumed for elevator in self.state.elevators)
|
||||||
|
|
||||||
if not completed:
|
if not completed:
|
||||||
return PerformanceMetrics(
|
return PerformanceMetrics(
|
||||||
completed_passengers=0,
|
completed_passengers=0,
|
||||||
@@ -560,6 +576,7 @@ class ElevatorSimulation:
|
|||||||
p95_floor_wait_time=0,
|
p95_floor_wait_time=0,
|
||||||
average_arrival_wait_time=0,
|
average_arrival_wait_time=0,
|
||||||
p95_arrival_wait_time=0,
|
p95_arrival_wait_time=0,
|
||||||
|
total_energy_consumption=total_energy,
|
||||||
)
|
)
|
||||||
|
|
||||||
floor_wait_times = [float(p.floor_wait_time) for p in completed]
|
floor_wait_times = [float(p.floor_wait_time) for p in completed]
|
||||||
@@ -585,6 +602,7 @@ class ElevatorSimulation:
|
|||||||
p95_floor_wait_time=average_excluding_top_percent(floor_wait_times, 5),
|
p95_floor_wait_time=average_excluding_top_percent(floor_wait_times, 5),
|
||||||
average_arrival_wait_time=sum(arrival_wait_times) / len(arrival_wait_times) if arrival_wait_times else 0,
|
average_arrival_wait_time=sum(arrival_wait_times) / len(arrival_wait_times) if arrival_wait_times else 0,
|
||||||
p95_arrival_wait_time=average_excluding_top_percent(arrival_wait_times, 5),
|
p95_arrival_wait_time=average_excluding_top_percent(arrival_wait_times, 5),
|
||||||
|
total_energy_consumption=total_energy,
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_events(self, since_tick: int = 0) -> List[SimulationEvent]:
|
def get_events(self, since_tick: int = 0) -> List[SimulationEvent]:
|
||||||
|
|||||||
@@ -290,7 +290,7 @@ def generate_fire_evacuation_traffic(
|
|||||||
for floor in range(1, floors):
|
for floor in range(1, floors):
|
||||||
# 每层随机数量的人需要疏散
|
# 每层随机数量的人需要疏散
|
||||||
num_people = random.randint(people_per_floor[0], people_per_floor[1])
|
num_people = random.randint(people_per_floor[0], people_per_floor[1])
|
||||||
for i in range(num_people):
|
for _ in range(num_people):
|
||||||
# 在10个tick内陆续到达,模拟疏散的紧急性
|
# 在10个tick内陆续到达,模拟疏散的紧急性
|
||||||
arrival_tick = alarm_tick + random.randint(0, min(10, duration - alarm_tick - 1))
|
arrival_tick = alarm_tick + random.randint(0, min(10, duration - alarm_tick - 1))
|
||||||
if arrival_tick < duration:
|
if arrival_tick < duration:
|
||||||
@@ -791,10 +791,12 @@ def generate_traffic_file(scenario: str, output_file: str, scale: Optional[str]
|
|||||||
traffic_data = generator_func(**generator_params)
|
traffic_data = generator_func(**generator_params)
|
||||||
|
|
||||||
# 准备building配置
|
# 准备building配置
|
||||||
|
num_elevators = params["elevators"]
|
||||||
building_config = {
|
building_config = {
|
||||||
"floors": params["floors"],
|
"floors": params["floors"],
|
||||||
"elevators": params["elevators"],
|
"elevators": num_elevators,
|
||||||
"elevator_capacity": params["elevator_capacity"],
|
"elevator_capacity": params["elevator_capacity"],
|
||||||
|
"elevator_energy_rates": [1.0] * num_elevators, # 每台电梯的能耗率,默认为1.0
|
||||||
"scenario": scenario,
|
"scenario": scenario,
|
||||||
"scale": scale,
|
"scale": scale,
|
||||||
"description": f"{config['description']} ({scale}规模)",
|
"description": f"{config['description']} ({scale}规模)",
|
||||||
@@ -835,7 +837,7 @@ def generate_scaled_traffic_files(
|
|||||||
if custom_building:
|
if custom_building:
|
||||||
floors = custom_building.get("floors", BUILDING_SCALES[scale]["floors"][0])
|
floors = custom_building.get("floors", BUILDING_SCALES[scale]["floors"][0])
|
||||||
elevators = custom_building.get("elevators", BUILDING_SCALES[scale]["elevators"][0])
|
elevators = custom_building.get("elevators", BUILDING_SCALES[scale]["elevators"][0])
|
||||||
elevator_capacity = custom_building.get("capacity", BUILDING_SCALES[scale]["capacity"][0])
|
_elevator_capacity = custom_building.get("capacity", BUILDING_SCALES[scale]["capacity"][0])
|
||||||
|
|
||||||
# 重新确定规模
|
# 重新确定规模
|
||||||
detected_scale = determine_building_scale(floors, elevators)
|
detected_scale = determine_building_scale(floors, elevators)
|
||||||
|
|||||||
Reference in New Issue
Block a user