7 Commits
0.0.6 ... 0.0.9

Author SHA1 Message Date
Xuwznln
b4b99daead Bump version: 0.0.8 → 0.0.9 2025-10-16 01:20:39 +08:00
Xuwznln
d44ba8b6cd Update gitignore 2025-10-16 01:20:11 +08:00
Xuwznln
71e8f2a451 Feat: add energy rate for elevators 2025-10-15 20:46:47 +08:00
Xuwznln
4b60359894 Bump version: 0.0.7 → 0.0.8 2025-10-12 02:14:27 +08:00
Xuwznln
0157496e6f Fix: client completed_passengers calculation error 2025-10-12 02:14:18 +08:00
Xuwznln
1031e677e1 Bump version: 0.0.6 → 0.0.7 2025-10-09 16:49:15 +08:00
Xuwznln
889d554f19 Fix: remove abstractmethod decroation for on_elevator_move 2025-10-09 16:49:07 +08:00
9 changed files with 44 additions and 16 deletions

View File

@@ -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
View File

@@ -1,6 +1,7 @@
# ================================ # ================================
# Python-related files # Python-related files
# ================================ # ================================
elevator_saga/traffic/test_cases.py
# Compiled Python files # Compiled Python files
__pycache__/ __pycache__/

View File

@@ -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
---------------- ----------------

View File

@@ -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"

View File

@@ -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:

View File

@@ -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):

View File

@@ -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)

View File

@@ -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]:

View File

@@ -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)