mirror of
https://github.com/ZGCA-Forge/Elevator.git
synced 2025-12-14 13:04:41 +00:00
985 lines
38 KiB
Python
985 lines
38 KiB
Python
#!/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, 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) -> int:
|
||
"""生成单个流量文件,支持规模化配置"""
|
||
if scenario not in TRAFFIC_SCENARIOS:
|
||
raise ValueError(f"Unknown scenario: {scenario}. Available: {list(TRAFFIC_SCENARIOS.keys())}")
|
||
|
||
config = 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
|
||
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_signature = inspect.signature(config["generator"])
|
||
generator_params = {k: v for k, v in params.items() if k in generator_signature.parameters}
|
||
traffic_data = config["generator"](**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") 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,
|
||
):
|
||
"""生成按规模分类的流量文件"""
|
||
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
|
||
):
|
||
"""为指定规模生成所有适合的场景文件"""
|
||
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, config in TRAFFIC_SCENARIOS.items():
|
||
# 检查场景是否适合该规模
|
||
if scale not in config["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,
|
||
):
|
||
"""生成所有场景的流量文件 - 保持向后兼容"""
|
||
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():
|
||
"""主函数 - 命令行接口"""
|
||
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()
|