import pandas as pd
import numpy as np
from datetime import datetime, timedelta
# 基础参数(文档约束)
CHANGE_TIME = 3.5 # 换模时间(小时)
REST_PERIODS = [ # 每日休息时段(小时)
(12, 12.5), # 12:00-12:30
(16.5, 17), # 16:30-17:00
(0, 1) # 0:00-1:00
]
class Order:
"""订单类:封装订单属性与计算逻辑"""
def __init__(self, order_id, product_id, quantity, delivery_date, capacity):
self.order_id = order_id # 订单号
self.product_id = product_id # 产品号
self.quantity = quantity # 数量(pcs)
self.delivery_date = delivery_date # 发货日期(datetime)
self.capacity = capacity # 产能(pcs/h)
self.processing_hours = quantity / capacity # 生产工时(h)
self.start_time = None # 开始时间
self.end_time = None # 结束时间
self.machine = None # 分配机台
self.delay_days = 0 # 延期天数
self.satisfaction = 10.0 # 满意度(初始10分)
class Machine:
"""机台类:封装机台属性与状态"""
def __init__(self, machine_id):
self.machine_id = machine_id # 机台号
self.available_time = datetime.now() # 可用时间
self.last_product = None # 上一生产产品
self.adjacent = [] # 相邻机台列表
def load_data():
"""加载模拟数据(对应问题中的附件表格)"""
# 1. 订单数据(产品号、数量、发货日期等)
orders_df = pd.DataFrame({
"订单号": ["PO001", "PO002", "PO003", "PO004"],
"产品号": ["P1", "P1", "P2", "P2"],
"订单数量": [1050, 2100, 24, 48],
"发货日期": ["2025-03-01", "2025-03-05", "2025-03-03", "2025-03-10"],
"产能(pcs/h)": [105, 105, 6, 6]
})
orders_df["发货日期"] = pd.to_datetime(orders_df["发货日期"])
# 2. 产品-机台关系(哪些机台可生产该产品)
product_machines = {
"P1": ["M01", "M02"], # P1可在M01、M02生产
"P2": ["50T", "50T-2"] # P2可在50T、50T-2生产
}
# 3. 产品属性(1:易搭配,3:难生产)
product_attrs = {"P1": 1, "P2": 3}
# 4. 机台数据(含相邻关系)
machines = [Machine("50T"), Machine("50T-2"), Machine("M01"), Machine("M02")]
machines[2].adjacent = ["M02"] # M01相邻机台为M02
machines[3].adjacent = ["M01"] # M02相邻机台为M01
# 5. 放假日期
holidays = {datetime(2025, 3, 6).date()} # 3月6日放假
return orders_df, product_machines, product_attrs, machines, holidays
def calculate_end_time(start_time, processing_hours, holidays):
"""
计算考虑休息和放假后的生产结束时间
:param start_time: 计划开始时间(datetime)
:param processing_hours: 生产工时(h)
:param holidays: 放假日期集合(date)
:return: 实际结束时间(datetime)
"""
current = start_time
remaining = processing_hours
while remaining > 0:
# 跳过放假日期
if current.date() in holidays:
current = datetime.combine(current.date() + timedelta(days=1), datetime.min.time())
continue
# 当天有效生产时段(扣除休息)
day_start = datetime.combine(current.date(), datetime.min.time())
intervals = [] # 有效时段列表:(开始, 结束)
prev_end = day_start
for rest_start, rest_end in REST_PERIODS:
# 休息前的有效时段
intervals.append((prev_end, day_start + timedelta(hours=rest_start)))
# 更新上一结束时间为休息结束
prev_end = day_start + timedelta(hours=rest_end)
intervals.append((prev_end, day_start + timedelta(hours=24))) # 最后一个时段
# 消耗有效时间
for (s, e) in intervals:
if current < s:
current = s # 等待有效时段开始
if current >= e:
continue # 时段已过
# 本时段可生产时间
available = (e - current).total_seconds() / 3600 # 转换为小时
use = min(remaining, available)
current += timedelta(hours=use)
remaining -= use
if remaining <= 0:
return current # 生产完成
# 进入下一天
current = datetime.combine(current.date() + timedelta(days=1), datetime.min.time())
return current
def basic_scheduling(orders_df, product_machines, machines, holidays):
"""基础排程:按发货日期优先级分配机台"""
# 初始化订单对象
orders = [Order(
row["订单号"], row["产品号"], row["订单数量"],
row["发货日期"], row["产能(pcs/h)"]
) for _, row in orders_df.iterrows()]
# 按发货日期排序(优先级:日期早的先生产)
orders.sort(key=lambda x: x.delivery_date)
for order in orders:
# 筛选可生产该机台的订单
candidate_machines = [
m for m in machines
if m.machine_id in product_machines[order.product_id]
]
# 选择最优机台(最早结束生产)
best_machine = None
best_end = None
for machine in candidate_machines:
# 计算换模时间(不同产品需换模)
change = CHANGE_TIME if machine.last_product != order.product_id else 0
start = machine.available_time + timedelta(hours=change)
# 计算结束时间(考虑休息和放假)
end = calculate_end_time(start, order.processing_hours, holidays)
# 更新最优机台
if best_end is None or end < best_end:
best_end = end
best_machine = machine
# 分配机台并更新状态
if best_machine:
order.machine = best_machine.machine_id
order.start_time = best_machine.available_time + timedelta(hours=CHANGE_TIME if best_machine.last_product != order.product_id else 0)
order.end_time = best_end
# 计算延期天数与满意度
order.delay_days = max(0, (order.end_time.date() - order.delivery_date.date()).days)
order.satisfaction = max(0, 10 - (order.delay_days // 3) * 0.1)
# 更新机台状态
best_machine.available_time = best_end
best_machine.last_product = order.product_id
# 统计结果
delay_orders = [o for o in orders if o.delay_days > 0]
return {
"延期订单数": len(delay_orders),
"最长延期(天)": max([o.delay_days for o in delay_orders], default=0),
"平均延期(天)": np.mean([o.delay_days for o in delay_orders]) if delay_orders else 0,
"平均满意度": np.mean([o.satisfaction for o in orders]),
"最差满意度": min([o.satisfaction for o in orders])
}