Files
Elevator/elevator_saga/traffic/generators.py
2025-10-01 17:07:31 +08:00

986 lines
39 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
Traffic Pattern Generators for Elevator Simulation
Generate JSON traffic files for different scenarios with scalable building sizes
From small (1 elevator, 3 floors, 10 people) to large (4 elevators, 12 floors, 200 people)
"""
import json
import math
import os.path
import random
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional
# 建筑规模配置
BUILDING_SCALES = {
"small": {
"floors": (3, 5),
"elevators": (1, 2),
"capacity": (6, 8),
"max_people": (10, 40),
"duration_range": (120, 240),
"description": "小型建筑 - 1-2台电梯3-5楼10-40人",
},
"medium": {
"floors": (6, 9),
"elevators": (2, 3),
"capacity": (8, 12),
"max_people": (40, 120),
"duration_range": (200, 400),
"description": "中型建筑 - 2-3台电梯6-9楼40-120人",
},
"large": {
"floors": (10, 12),
"elevators": (3, 4),
"capacity": (10, 15),
"max_people": (120, 200),
"duration_range": (300, 600),
"description": "大型建筑 - 3-4台电梯10-12楼120-200人",
},
}
def calculate_intensity_for_scale(base_intensity: float, floors: int, target_people: int, duration: int) -> float:
"""根据建筑规模计算合适的流量强度"""
# 估算每tick平均产生的人数
estimated_people_per_tick = base_intensity
total_estimated = estimated_people_per_tick * duration
if total_estimated <= 0:
return base_intensity
# 调整强度以达到目标人数
adjustment_factor = target_people / total_estimated
return min(1.0, base_intensity * adjustment_factor)
def limit_traffic_count(traffic: List[Dict[str, Any]], max_people: int) -> List[Dict[str, Any]]:
"""限制流量中的人数不超过最大值"""
if len(traffic) <= max_people:
return traffic
# 按时间排序,优先保留早期的乘客
traffic_sorted = sorted(traffic, key=lambda x: x["tick"])
return traffic_sorted[:max_people]
def generate_up_peak_traffic(
floors: int = 10, duration: int = 300, intensity: float = 0.6, max_people: int = 100, seed: int = 42
) -> List[Dict[str, Any]]:
"""生成上行高峰流量 - 主要从底层到高层"""
random.seed(seed)
traffic = []
passenger_id = 1
# 根据目标人数调整强度
adjusted_intensity = calculate_intensity_for_scale(intensity, floors, max_people, duration)
for tick in range(duration):
# 根据时间调整强度 - 早期高峰
time_factor = 1.0 + 0.5 * math.sin(tick * math.pi / duration)
current_intensity = adjusted_intensity * time_factor
if random.random() < current_intensity:
# 针对小建筑调整比例 - 小建筑大厅使用更频繁
lobby_ratio = 0.95 if floors <= 5 else 0.9
if random.random() < lobby_ratio:
origin = 0
destination = random.randint(1, floors - 1)
else:
# 其他楼层间流量
if floors > 2:
origin = random.randint(1, min(floors - 2, floors - 1))
destination = random.randint(origin + 1, floors - 1)
else:
origin = 0
destination = floors - 1
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
return limit_traffic_count(traffic, max_people)
def generate_down_peak_traffic(
floors: int = 10, duration: int = 300, intensity: float = 0.6, max_people: int = 100, seed: int = 42
) -> List[Dict[str, Any]]:
"""生成下行高峰流量 - 主要从高层到底层"""
random.seed(seed)
traffic = []
passenger_id = 1
# 根据目标人数调整强度
adjusted_intensity = calculate_intensity_for_scale(intensity, floors, max_people, duration)
for tick in range(duration):
# 根据时间调整强度 - 后期高峰
time_factor = 1.0 + 0.5 * math.sin((tick + duration / 2) * math.pi / duration)
current_intensity = adjusted_intensity * time_factor
if random.random() < current_intensity:
# 针对小建筑调整比例 - 小建筑到大厅更频繁
lobby_ratio = 0.95 if floors <= 5 else 0.9
if random.random() < lobby_ratio:
origin = random.randint(1, floors - 1)
destination = 0
else:
# 其他楼层间流量
if floors > 2:
origin = random.randint(2, floors - 1)
destination = random.randint(1, origin - 1)
else:
origin = floors - 1
destination = 0
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
return limit_traffic_count(traffic, max_people)
def generate_inter_floor_traffic(
floors: int = 10, duration: int = 400, intensity: float = 0.4, max_people: int = 80, seed: int = 42
) -> List[Dict[str, Any]]:
"""生成楼层间流量 - 主要楼层间移动,适合小建筑"""
random.seed(seed)
traffic = []
passenger_id = 1
# 小建筑更适合这种场景,调整强度
if floors <= 5:
adjusted_intensity = calculate_intensity_for_scale(intensity * 1.2, floors, max_people, duration)
else:
adjusted_intensity = calculate_intensity_for_scale(intensity, floors, max_people, duration)
for tick in range(duration):
# 平稳的流量,轻微波动
time_variation = 1.0 + 0.2 * math.sin(tick * 2 * math.pi / duration)
current_intensity = adjusted_intensity * time_variation
if random.random() < current_intensity:
if floors <= 3:
# 超小建筑,允许包含大厅
origin = random.randint(0, floors - 1)
destination = random.choice([f for f in range(floors) if f != origin])
else:
# 其他建筑,避免大厅
origin = random.randint(1, floors - 1)
destination = random.choice([f for f in range(1, floors) if f != origin])
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
return limit_traffic_count(traffic, max_people)
def generate_lunch_rush_traffic(
floors: int = 10, duration: int = 200, intensity: float = 0.7, max_people: int = 60, seed: int = 42
) -> List[Dict[str, Any]]:
"""生成午餐时间流量 - 双向流量,适合中大型建筑"""
random.seed(seed)
traffic = []
passenger_id = 1
# 小建筑没有餐厅概念,生成简单的双向流量
if floors <= 5:
# 小建筑简化为楼层间随机流量
adjusted_intensity = calculate_intensity_for_scale(intensity * 0.6, floors, max_people, duration)
for tick in range(duration):
# 高斯分布的流量强度
peak_center = duration // 2
peak_width = duration // 4
distance_from_peak = abs(tick - peak_center) / peak_width
current_intensity = adjusted_intensity * max(0.3, math.exp(-distance_from_peak * distance_from_peak))
if random.random() < current_intensity:
origin = random.randint(0, floors - 1)
destination = random.choice([f for f in range(floors) if f != origin])
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
else:
# 中大型建筑假设1-2楼是餐厅3+楼是办公室
restaurant_floors = [1, 2] if floors > 2 else [1]
office_floors = list(range(max(3, len(restaurant_floors) + 1), floors))
adjusted_intensity = calculate_intensity_for_scale(intensity, floors, max_people, duration)
for tick in range(duration):
# 高斯分布的流量强度
peak_center = duration // 2
peak_width = duration // 4
distance_from_peak = abs(tick - peak_center) / peak_width
current_intensity = adjusted_intensity * max(0.2, math.exp(-distance_from_peak * distance_from_peak))
if random.random() < current_intensity:
if office_floors and random.random() < 0.5:
# 去餐厅
origin = random.choice(office_floors)
destination = random.choice(restaurant_floors)
else:
# 回办公室
origin = random.choice(restaurant_floors)
destination = random.choice(office_floors) if office_floors else 0
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
return limit_traffic_count(traffic, max_people)
def generate_random_traffic(
floors: int = 10, duration: int = 500, intensity: float = 0.3, max_people: int = 80, seed: int = 42
) -> List[Dict[str, Any]]:
"""生成随机流量 - 均匀分布,适合所有规模建筑"""
random.seed(seed)
traffic = []
passenger_id = 1
# 根据目标人数调整强度
adjusted_intensity = calculate_intensity_for_scale(intensity, floors, max_people, duration)
for tick in range(duration):
# 添加轻微的时间变化,避免完全平坦
time_variation = 1.0 + 0.1 * math.sin(tick * 4 * math.pi / duration)
current_intensity = adjusted_intensity * time_variation
if random.random() < current_intensity:
origin = random.randint(0, floors - 1)
destination = random.choice([f for f in range(floors) if f != origin])
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
return limit_traffic_count(traffic, max_people)
def generate_fire_evacuation_traffic(
floors: int = 10, duration: int = 150, max_people: int = 120, seed: int = 42
) -> List[Dict[str, Any]]:
"""生成火警疏散流量 - 紧急疏散到大厅"""
random.seed(seed)
traffic = []
passenger_id = 1
# 正常时间段
normal_duration = duration // 3
# 正常流量 - 较少
normal_intensity = 0.15
for tick in range(normal_duration):
if random.random() < normal_intensity:
origin = random.randint(0, floors - 1)
destination = random.choice([f for f in range(floors) if f != origin])
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
# 火警开始 - 大量疏散到大厅
alarm_tick = normal_duration
# 根据建筑规模调整每层人数
if floors <= 5:
people_per_floor = (2, 4) # 小建筑每层2-4人
elif floors <= 9:
people_per_floor = (3, 6) # 中建筑每层3-6人
else:
people_per_floor = (4, 8) # 大建筑每层4-8人
for floor in range(1, floors):
# 每层随机数量的人需要疏散
num_people = random.randint(people_per_floor[0], people_per_floor[1])
for i in range(num_people):
# 在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}) # 疏散到大厅
passenger_id += 1
return limit_traffic_count(traffic, max_people)
def generate_mixed_scenario_traffic(
floors: int = 10, duration: int = 600, max_people: int = 150, seed: int = 42
) -> List[Dict[str, Any]]:
"""生成混合场景流量 - 包含多种模式,适合中大型建筑"""
random.seed(seed)
traffic = []
passenger_id = 1
# 根据人数目标调整各阶段强度
target_per_phase = max_people // 4
# 第一阶段:上行高峰 (0-25%)
phase1_end = duration // 4
phase1_intensity = calculate_intensity_for_scale(0.7, floors, target_per_phase, phase1_end)
for tick in range(phase1_end):
if random.random() < phase1_intensity:
lobby_ratio = 0.9 if floors > 5 else 0.95
if random.random() < lobby_ratio:
origin = 0
destination = random.randint(1, floors - 1)
else:
if floors > 2:
origin = random.randint(0, floors - 2)
destination = random.randint(origin + 1, floors - 1)
else:
origin = 0
destination = floors - 1
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
# 第二阶段:正常流量 (25%-50%)
phase2_end = duration // 2
phase2_intensity = calculate_intensity_for_scale(0.3, floors, target_per_phase, phase2_end - phase1_end)
for tick in range(phase1_end, phase2_end):
if random.random() < phase2_intensity:
origin = random.randint(0, floors - 1)
destination = random.choice([f for f in range(floors) if f != origin])
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
# 第三阶段:午餐/中峰流量 (50%-67%)
phase3_end = phase2_end + duration // 6
phase3_intensity = calculate_intensity_for_scale(0.6, floors, target_per_phase, phase3_end - phase2_end)
for tick in range(phase2_end, phase3_end):
if random.random() < phase3_intensity:
if floors > 5 and random.random() < 0.6:
# 餐厅流量 - 仅适用于大型建筑
if random.random() < 0.5:
origin = random.randint(3, floors - 1)
destination = random.randint(1, 2)
else:
origin = random.randint(1, 2)
destination = random.randint(3, floors - 1)
else:
# 其他流量
origin = random.randint(0, floors - 1)
destination = random.choice([f for f in range(floors) if f != origin])
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
# 第四阶段:下行高峰 (67%-100%)
phase4_intensity = calculate_intensity_for_scale(0.6, floors, target_per_phase, duration - phase3_end)
for tick in range(phase3_end, duration):
if random.random() < phase4_intensity:
lobby_ratio = 0.85 if floors > 5 else 0.9
if random.random() < lobby_ratio:
origin = random.randint(1, floors - 1)
destination = 0
else:
if floors > 2:
origin = random.randint(2, floors - 1)
destination = random.randint(1, origin - 1)
else:
origin = floors - 1
destination = 0
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
return limit_traffic_count(traffic, max_people)
def generate_high_density_traffic(
floors: int = 10, duration: int = 300, intensity: float = 1.2, max_people: int = 200, seed: int = 42
) -> List[Dict[str, Any]]:
"""生成高密度流量 - 压力测试,适合测试电梯系统极限"""
random.seed(seed)
traffic = []
passenger_id = 1
# 计算目标强度,确保不超过人数限制
target_people_per_tick = max_people / duration
safe_intensity = min(intensity, target_people_per_tick * 1.5) # 留出一些余量
for tick in range(duration):
# 高强度的随机流量,使用高斯分布增加变化
base_passengers = safe_intensity
variation = random.gauss(0, safe_intensity * 0.3) # 30%变化
num_passengers = max(0, int(base_passengers + variation))
for _ in range(num_passengers):
origin = random.randint(0, floors - 1)
destination = random.choice([f for f in range(floors) if f != origin])
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
# 提前检查,避免生成过多乘客
if len(traffic) >= max_people:
break
if len(traffic) >= max_people:
break
return limit_traffic_count(traffic, max_people)
def generate_small_building_traffic(
floors: int = 4, duration: int = 180, intensity: float = 0.4, max_people: int = 25, seed: int = 42
) -> List[Dict[str, Any]]:
"""生成小建筑专用流量 - 简单楼层间移动适合3-5层建筑"""
random.seed(seed)
traffic = []
passenger_id = 1
# 小建筑特点:频繁使用大厅,简单的上下楼
adjusted_intensity = calculate_intensity_for_scale(intensity, floors, max_people, duration)
for tick in range(duration):
# 轻微的时间变化
time_factor = 1.0 + 0.3 * math.sin(tick * 2 * math.pi / duration)
current_intensity = adjusted_intensity * time_factor
if random.random() < current_intensity:
# 80%涉及大厅的移动
if random.random() < 0.8:
if random.random() < 0.5:
# 从大厅上楼
origin = 0
destination = random.randint(1, floors - 1)
else:
# 下到大厅
origin = random.randint(1, floors - 1)
destination = 0
else:
# 楼层间移动
origin = random.randint(1, floors - 1)
destination = random.choice([f for f in range(1, floors) if f != origin])
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
return limit_traffic_count(traffic, max_people)
def generate_medical_building_traffic(
floors: int = 8, duration: int = 240, intensity: float = 0.5, max_people: int = 80, seed: int = 42
) -> List[Dict[str, Any]]:
"""生成医疗建筑流量 - 模拟医院/诊所的特殊流量模式"""
random.seed(seed)
traffic = []
passenger_id = 1
# 医疗建筑特点:大厅使用频繁,某些楼层(如手术室)访问较少
adjusted_intensity = calculate_intensity_for_scale(intensity, floors, max_people, duration)
# 定义楼层类型权重 - 大厅和低层更频繁
floor_weights = []
for floor in range(floors):
if floor == 0: # 大厅 - 最高权重
weight = 3.0
elif floor <= 2: # 急诊、门诊 - 高权重
weight = 2.0
elif floor <= floors - 2: # 普通病房 - 中等权重
weight = 1.0
else: # 手术室、ICU - 低权重
weight = 0.3
floor_weights.append(weight)
for tick in range(duration):
# 医疗建筑通常有明显的时间模式
time_factor = 1.0 + 0.4 * math.sin((tick + duration * 0.2) * math.pi / duration)
current_intensity = adjusted_intensity * time_factor
if random.random() < current_intensity:
# 85%的移动涉及大厅
if random.random() < 0.85:
if random.random() < 0.6:
# 从大厅到其他楼层
origin = 0
# 使用权重选择目标楼层
destinations = list(range(1, floors))
weights = floor_weights[1:]
destination = random.choices(destinations, weights=weights)[0]
else:
# 从其他楼层到大厅
origins = list(range(1, floors))
weights = floor_weights[1:]
origin = random.choices(origins, weights=weights)[0]
destination = 0
else:
# 楼层间移动(较少)
floor_candidates = list(range(floors))
origin = random.choice(floor_candidates)
destination = random.choice([f for f in floor_candidates if f != origin])
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
return limit_traffic_count(traffic, max_people)
def generate_meeting_event_traffic(
floors: int = 6, duration: int = 150, intensity: float = 0.8, max_people: int = 50, seed: int = 42
) -> List[Dict[str, Any]]:
"""生成会议事件流量 - 模拟大型会议开始和结束的流量模式"""
random.seed(seed)
traffic = []
passenger_id = 1
# 假设会议在某个楼层举行
meeting_floor = floors // 2 if floors > 2 else 1
# 会议分为三个阶段:到达、中间、离开
arrival_end = duration // 3
departure_start = duration * 2 // 3
for tick in range(duration):
should_add_passenger = False
origin = 0
destination = 0
if tick < arrival_end:
# 到达阶段 - 大量人员前往会议楼层
phase_progress = tick / arrival_end
current_intensity = intensity * (1.0 + math.sin(phase_progress * math.pi))
if random.random() < current_intensity:
# 主要从大厅到会议楼层
if random.random() < 0.9:
origin = 0
destination = meeting_floor
else:
# 少量其他楼层间移动
origin = random.randint(0, floors - 1)
destination = random.choice([f for f in range(floors) if f != origin])
should_add_passenger = True
elif tick >= departure_start:
# 离开阶段 - 大量人员从会议楼层离开
phase_progress = (tick - departure_start) / (duration - departure_start)
current_intensity = intensity * (1.0 + math.sin(phase_progress * math.pi))
if random.random() < current_intensity:
# 主要从会议楼层到大厅
if random.random() < 0.9:
origin = meeting_floor
destination = 0
else:
# 少量其他移动
origin = random.randint(0, floors - 1)
destination = random.choice([f for f in range(floors) if f != origin])
should_add_passenger = True
else:
# 中间阶段 - 低流量
if random.random() < intensity * 0.1:
origin = random.randint(0, floors - 1)
destination = random.choice([f for f in range(floors) if f != origin])
should_add_passenger = True
if should_add_passenger:
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
return limit_traffic_count(traffic, max_people)
def generate_progressive_test_traffic(
floors: int = 8, duration: int = 400, max_people: int = 100, seed: int = 42
) -> List[Dict[str, Any]]:
"""生成渐进式测试流量 - 从低强度逐渐增加到高强度"""
random.seed(seed)
traffic = []
passenger_id = 1
# 分为四个阶段,强度逐渐增加
stage_duration = duration // 4
for stage in range(4):
stage_start = stage * stage_duration
stage_end = min((stage + 1) * stage_duration, duration)
stage_intensity = 0.2 + stage * 0.25 # 0.2, 0.45, 0.7, 0.95
stage_target = max_people // 4
adjusted_intensity = calculate_intensity_for_scale(stage_intensity, floors, stage_target, stage_duration)
for tick in range(stage_start, stage_end):
# 每个阶段内部也有变化
local_progress = (tick - stage_start) / stage_duration
time_factor = 1.0 + 0.3 * math.sin(local_progress * 2 * math.pi)
current_intensity = adjusted_intensity * time_factor
if random.random() < current_intensity:
origin = random.randint(0, floors - 1)
destination = random.choice([f for f in range(floors) if f != origin])
traffic.append({"id": passenger_id, "origin": origin, "destination": destination, "tick": tick})
passenger_id += 1
return limit_traffic_count(traffic, max_people)
# 按建筑规模分类的场景配置
TRAFFIC_SCENARIOS = {
# 经典场景 - 适用于所有规模,会根据建筑规模自动调整
"up_peak": {
"generator": generate_up_peak_traffic,
"description": "上行高峰 - 主要从底层到高层",
"scales": {
"small": {"intensity": 0.5, "max_people": 20},
"medium": {"intensity": 0.6, "max_people": 80},
"large": {"intensity": 0.7, "max_people": 150},
},
"suitable_scales": ["small", "medium", "large"],
},
"down_peak": {
"generator": generate_down_peak_traffic,
"description": "下行高峰 - 主要从高层到底层",
"scales": {
"small": {"intensity": 0.5, "max_people": 20},
"medium": {"intensity": 0.6, "max_people": 80},
"large": {"intensity": 0.7, "max_people": 150},
},
"suitable_scales": ["small", "medium", "large"],
},
"inter_floor": {
"generator": generate_inter_floor_traffic,
"description": "楼层间流量 - 适合小建筑",
"scales": {
"small": {"intensity": 0.6, "max_people": 30},
"medium": {"intensity": 0.4, "max_people": 60},
"large": {"intensity": 0.3, "max_people": 80},
},
"suitable_scales": ["small", "medium", "large"],
},
"lunch_rush": {
"generator": generate_lunch_rush_traffic,
"description": "午餐时间流量 - 双向流量,适合中大型建筑",
"scales": {
"small": {"intensity": 0.4, "max_people": 25},
"medium": {"intensity": 0.7, "max_people": 60},
"large": {"intensity": 0.8, "max_people": 100},
},
"suitable_scales": ["medium", "large"],
},
"random": {
"generator": generate_random_traffic,
"description": "随机流量 - 均匀分布,适合所有规模",
"scales": {
"small": {"intensity": 0.4, "max_people": 25},
"medium": {"intensity": 0.3, "max_people": 80},
"large": {"intensity": 0.25, "max_people": 120},
},
"suitable_scales": ["small", "medium", "large"],
},
"fire_evacuation": {
"generator": generate_fire_evacuation_traffic,
"description": "火警疏散 - 紧急疏散到大厅",
"scales": {"small": {"max_people": 20}, "medium": {"max_people": 70}, "large": {"max_people": 120}},
"suitable_scales": ["small", "medium", "large"],
},
"mixed_scenario": {
"generator": generate_mixed_scenario_traffic,
"description": "混合场景 - 包含多种流量模式,适合中大型建筑",
"scales": {"medium": {"max_people": 100}, "large": {"max_people": 180}},
"suitable_scales": ["medium", "large"],
},
"high_density": {
"generator": generate_high_density_traffic,
"description": "高密度流量 - 压力测试",
"scales": {
"small": {"intensity": 0.8, "max_people": 35},
"medium": {"intensity": 1.0, "max_people": 120},
"large": {"intensity": 1.2, "max_people": 200},
},
"suitable_scales": ["small", "medium", "large"],
},
# 新增的专用场景
"small_building": {
"generator": generate_small_building_traffic,
"description": "小建筑专用 - 简单楼层间移动",
"scales": {"small": {"intensity": 0.4, "max_people": 25}},
"suitable_scales": ["small"],
},
"medical": {
"generator": generate_medical_building_traffic,
"description": "医疗建筑 - 特殊流量模式",
"scales": {"medium": {"intensity": 0.5, "max_people": 80}, "large": {"intensity": 0.6, "max_people": 120}},
"suitable_scales": ["medium", "large"],
},
"meeting_event": {
"generator": generate_meeting_event_traffic,
"description": "会议事件 - 集中到达和离开",
"scales": {
"small": {"intensity": 0.6, "max_people": 30},
"medium": {"intensity": 0.8, "max_people": 50},
"large": {"intensity": 1.0, "max_people": 80},
},
"suitable_scales": ["small", "medium", "large"],
},
"progressive_test": {
"generator": generate_progressive_test_traffic,
"description": "渐进式测试 - 强度逐渐增加",
"scales": {"small": {"max_people": 40}, "medium": {"max_people": 100}, "large": {"max_people": 150}},
"suitable_scales": ["small", "medium", "large"],
},
}
def determine_building_scale(floors: int, elevators: int) -> str:
"""根据楼层数和电梯数确定建筑规模"""
if floors <= 5 and elevators <= 2:
return "small"
elif floors <= 9 and elevators <= 3:
return "medium"
else:
return "large"
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: Dict[str, Any] = TRAFFIC_SCENARIOS[scenario]
# 确定建筑规模
if scale is None:
floors = kwargs.get("floors", 6) # 默认中等规模
elevators = kwargs.get("elevators", 2)
scale = determine_building_scale(floors, elevators)
# 检查场景是否适合该规模
if scale not in config["suitable_scales"]:
print(
f"Warning: Scenario '{scenario}' not recommended for scale '{scale}'. Suitable scales: {config['suitable_scales']}"
)
# 选择最接近的适合规模
if "medium" in config["suitable_scales"]:
scale = "medium"
else:
scale = config["suitable_scales"][0]
# 获取规模特定的参数
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 = {}
# 设置默认参数
params["floors"] = kwargs.get("floors", building_scale["floors"][0])
params["elevators"] = kwargs.get("elevators", building_scale["elevators"][0])
params["elevator_capacity"] = kwargs.get("elevator_capacity", building_scale["capacity"][0])
# 设置场景相关参数
params["duration"] = kwargs.get("duration", building_scale["duration_range"][0])
params["intensity"] = scale_params.get("intensity", 0.5)
params["max_people"] = scale_params.get("max_people", building_scale["max_people"][0])
params["seed"] = kwargs.get("seed", 42)
# 允许kwargs完全覆盖
params.update(kwargs)
# 生成流量数据 - 只传递生成器函数需要的参数
import inspect
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 = generator_func(**generator_params)
# 准备building配置
building_config = {
"floors": params["floors"],
"elevators": params["elevators"],
"elevator_capacity": params["elevator_capacity"],
"scenario": scenario,
"scale": scale,
"description": f"{config['description']} ({scale}规模)",
"expected_passengers": len(traffic_data),
"duration": params["duration"],
}
# 组合完整的数据结构
complete_data = {"building": building_config, "traffic": traffic_data}
# 写入文件
with open(output_file, "w", encoding="utf-8") as f:
json.dump(complete_data, f, indent=2, ensure_ascii=False)
print(f"Generated {len(traffic_data)} passengers for scenario '{scenario}' ({scale}) -> {output_file}")
return len(traffic_data)
def generate_scaled_traffic_files(
output_dir: str,
scale: str = "medium",
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)
if generate_all_scales:
# 生成所有规模的文件
for scale_name in ["small", "medium", "large"]:
scale_dir = output_path / scale_name
scale_dir.mkdir(exist_ok=True)
_generate_files_for_scale(scale_dir, scale_name, seed)
else:
# 只生成指定规模
if custom_building:
floors = custom_building.get("floors", BUILDING_SCALES[scale]["floors"][0])
elevators = custom_building.get("elevators", BUILDING_SCALES[scale]["elevators"][0])
elevator_capacity = custom_building.get("capacity", BUILDING_SCALES[scale]["capacity"][0])
# 重新确定规模
detected_scale = determine_building_scale(floors, elevators)
if detected_scale != scale:
print(f"Note: Building config suggests {detected_scale} scale, but {scale} was requested")
scale = detected_scale
_generate_files_for_scale(output_path, scale, seed, custom_building)
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
files_generated = 0
# 确定建筑参数
if custom_building:
floors = custom_building.get("floors", building_config["floors"][0])
elevators = custom_building.get("elevators", building_config["elevators"][0])
elevator_capacity = custom_building.get("capacity", building_config["capacity"][0])
else:
# 使用规模的默认配置
floors = building_config["floors"][0]
elevators = building_config["elevators"][0]
elevator_capacity = building_config["capacity"][0]
print(f"\nGenerating {scale} scale traffic files:")
print(f"Building: {floors} floors, {elevators} elevators, capacity {elevator_capacity}")
for scenario_name, scenario_config in TRAFFIC_SCENARIOS.items():
# 检查场景是否适合该规模
config_dict: Dict[str, Any] = scenario_config
if scale not in config_dict["suitable_scales"]:
continue
filename = f"{scenario_name}.json"
file_path = output_path / filename
# 准备参数
params = {
"floors": floors,
"elevators": elevators,
"elevator_capacity": elevator_capacity,
"seed": seed + hash(scenario_name) % 1000, # 为每个场景使用不同的seed
}
# 生成流量文件
try:
passenger_count = generate_traffic_file(scenario_name, str(file_path), scale=scale, **params)
total_passengers += passenger_count
files_generated += 1
except Exception as e:
print(f"Error generating {scenario_name}: {e}")
print(f"Generated {files_generated} traffic files for {scale} scale in {output_path}")
print(f"Total passengers: {total_passengers}")
print(
f"Average per scenario: {total_passengers/files_generated:.1f}" if files_generated > 0 else "No files generated"
)
def generate_all_traffic_files(
output_dir: str,
floors: int = 6,
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}
generate_scaled_traffic_files(output_dir=output_dir, scale=scale, seed=seed, custom_building=custom_building)
def main() -> None:
"""主函数 - 命令行接口"""
import argparse
parser = argparse.ArgumentParser(description="Generate scalable elevator traffic files")
parser.add_argument(
"--scale",
type=str,
choices=["small", "medium", "large"],
help="Building scale (overrides individual parameters)",
)
parser.add_argument(
"--all-scales", action="store_true", help="Generate files for all scales in separate directories"
)
parser.add_argument("--floors", type=int, help="Number of floors")
parser.add_argument("--elevators", type=int, help="Number of elevators")
parser.add_argument("--elevator-capacity", type=int, help="Elevator capacity")
parser.add_argument("--seed", type=int, default=42, help="Random seed")
parser.add_argument("--output-dir", type=str, default=None, help="Output directory (default: current directory)")
args = parser.parse_args()
output_dir = args.output_dir or os.path.dirname(__file__)
if args.all_scales:
# 生成所有规模的文件
generate_scaled_traffic_files(output_dir=output_dir, generate_all_scales=True, seed=args.seed)
elif args.scale:
# 生成指定规模的文件
custom_building = None
if args.floors or args.elevators or args.elevator_capacity:
custom_building = {}
if args.floors:
custom_building["floors"] = args.floors
if args.elevators:
custom_building["elevators"] = args.elevators
if args.elevator_capacity:
custom_building["capacity"] = args.elevator_capacity
generate_scaled_traffic_files(
output_dir=output_dir, scale=args.scale, seed=args.seed, custom_building=custom_building
)
else:
# 向后兼容模式:使用旧的参数
floors = args.floors or 6
elevators = args.elevators or 2
elevator_capacity = args.elevator_capacity or 8
generate_all_traffic_files(
output_dir=output_dir,
floors=floors,
elevators=elevators,
elevator_capacity=elevator_capacity,
seed=args.seed,
)
print("\nUsage examples:")
print(" # Generate all scales:")
print(" python generators.py --all-scales")
print(" # Generate small scale:")
print(" python generators.py --scale small")
print(" # Custom building (auto-detect scale):")
print(" python generators.py --floors 3 --elevators 1")
print(" # Force scale with custom config:")
print(" python generators.py --scale large --floors 12 --elevators 4")
if __name__ == "__main__":
main()