<think>我们将在现有代码基础上进行修改,主要增加导出详细排程结果的功能。原代码已经可以计算每个订单的详细排程信息,但未导出。我们将修改`problem3_scheduling`函数,使其返回每个订单的详细排程数据,然后新增一个导出函数,将详细结果按照要求输出到Excel。
具体步骤:
1. 在`problem3_scheduling`函数中,除了返回统计指标外,还要返回所有原始订单的详细排程信息列表。
2. 新增一个函数`export_detailed_schedule`,用于将详细排程信息导出为Excel,包含指定的列。
3. 修改主函数,在每次合单策略(7天、15天、30天)的排程后,导出详细排程结果到不同的Excel文件(或同一个文件的不同sheet)。
注意:原代码中已经计算了每个订单的以下信息:
- order_id (订单号PO)
- product_id (产品号)
- processing_hours (需要工时)
- start_time (生产计划开始时间)
- end_time (生产计划预计完成时间)
- delivery_date (发货日期)
- quantity (订单数量)
- weight (Weight/pcs(g)) 注意:原Order类没有weight属性,需要补充
- capacity (Capacity(pcs/h))
- 最迟开始时间:根据公式,最迟开始时间 = 发货日期 - 生产工时(按小时计算)
- 合单序号:在合并任务中,我们为每个合并任务设置了merged_id(原代码中没有,需要补充)
- 前置任务生产计划序号:在排程时,我们记录了每个任务的前置任务(原代码中没有记录,需要补充)
- 是否延期:根据delay_days>0判断
- 延期天数:delay_days
因此,我们需要对代码做以下调整:
a. 在Order类中增加weight属性(从数据加载时传入)。
b. 在合并任务时,为每个合并任务生成一个唯一的合单序号(merged_id),并关联到原始订单。
c. 在排程时,记录前置任务(即同一机台的上一个任务)的计划序号(plan number),并关联到当前任务中的每个订单。
由于原代码中已经实现了部分,但为了清晰,我们将补充缺失的部分。
修改点:
1. 在Order类中添加weight属性,并在初始化时赋值。
2. 在MergedOrder类中添加merged_id属性(原代码中已经添加,但我们在创建合并任务时需要传入)。
3. 在merge_orders函数中,为每个合并任务生成一个唯一的merged_id(可以使用计数器)。
4. 在problem3_scheduling函数中,为每个任务分配一个计划序号(plan number),并记录在任务和订单上。同时,记录前置任务(即机台的上一个任务)的计划序号。
由于原代码中已经有一些相关属性,但未完全实现,我们将按照以下方式补充:
- 在Machine类中添加一个属性last_plan_number,用于记录上一个任务的计划序号。
- 在排程过程中,为每个合并任务分配一个递增的计划序号(plan number),然后将这个序号赋给该任务的所有原始订单,同时将前置任务序号设置为机台的上一个任务的计划序号(即last_plan_number)。
然后,在problem3_scheduling函数中,我们收集每个订单的详细信息,并返回。
另外,我们还需要计算每个订单的“最迟开始时间”,公式为:最迟开始时间 = 发货日期 - 生产工时(按小时数计算的时间差)。注意:这个最迟开始时间是一个理论值,不考虑休息和放假。
由于原代码中已经计算了延期天数,所以是否延期和延期天数可以直接获取。
最后,我们将为每次合单策略(7天、15天、30天)生成一个详细排程表,并导出到Excel。
由于详细排程表包含的订单数量较大,我们为每个合单策略单独导出到一个Excel文件(或同一个文件的不同sheet)。这里我们选择导出到同一个文件的不同sheet。
实现步骤:
1. 修改Order类,添加weight属性。
2. 修改main函数中初始化订单的部分,将weight传入Order对象。
3. 修改merge_orders函数,使其为每个合并任务生成一个唯一的merged_id(使用一个计数器)。
4. 在problem3_scheduling函数中:
- 初始化一个plan_number计数器(从1开始)。
- 对于每个任务,分配一个计划序号(plan_number),然后递增。
- 在机台的状态更新中,记录当前任务的计划序号(作为下一个任务的前置任务序号)。
- 对于每个订单,设置plan_number(当前任务的计划序号)和forward_plan(机台上一个任务的计划序号,如果没有则为None)。
- 收集每个订单的详细信息(包括新增的字段)。
5. 修改problem3_scheduling的返回值为两个:所有订单的详细列表和统计信息字典。
6. 新增一个导出函数export_detailed_schedule,将详细排程列表导出到Excel,并按照指定列顺序。
列顺序要求(16列):
1. 订单号PO
2. 产品号
3. 需要工时 (Requested Time--Hours)
4. 生产计划开始时间(Plan Start Time)
5. 生产计划预计完成时间(Plan Finish Time)
6. 发货日期(DeliveryDate)
7. 订单数量
8. Weight/pcs(g)
9. Capacity(pcs/h)
10. 最迟开始时间 (Late Start Time)
11. 合单序号(Joined Order Number)
12. 前置任务生产计划序号(Forward Plan Number)
13. 是否延期
14. 延期天数
15. 计划序号(Plan Number) # 新增,每个任务一个序号
16. 生产计划安排机台号 # 新增
注意:原要求中还有“计划序号”和“生产计划安排机台号”,所以我们在详细结果中也要包含这两列。
由于原问题三要求输出的是不同合单策略的统计结果,现在我们要额外输出详细排程,因此主函数中每次排程后都要导出详细排程。
为了避免覆盖,我们将为每个合单策略的详细排程导出到同一个Excel文件的不同sheet中,sheet名为“7天合单排程”、“15天合单排程”、“30天合单排程”。
修改后的主函数流程:
for merge_days in merge_days_list:
复制机台和订单
合单
排程(得到详细订单列表和统计信息)
将详细订单列表转换为DataFrame
将DataFrame添加到ExcelWriter的一个sheet中(sheet名按合单天数命名)
记录统计信息
然后,再导出统计结果的Excel(原问题三的统计结果表)
由于原代码中已经有了一个导出统计结果的函数export_problem3_results,我们保留,同时新增一个导出详细排程的函数(在同一个Excel文件中写入多个sheet)。
注意:由于详细排程数据量可能很大,我们使用pd.ExcelWriter一次性写入多个sheet。
代码修改如下:</think>我将为您修改代码,增加输出详细排程信息的功能,同时保留原有的统计结果输出。以下是修改后的完整代码:
```python
import pandas as pd
import numpy as np
from datetime import datetime, timedelta, time
import os
# 基础参数(文档约束)
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, weight, attr):
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.weight = weight # 产品重量(g)
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分)
self.attr = attr # 产品属性(1:易搭配,3:难生产)
self.merged_id = None # 合单序号
self.plan_number = None # 计划序号
self.forward_plan = None # 前置任务计划序号
class MergedOrder:
"""合单任务类:封装合并后的生产任务"""
def __init__(self, product_id, total_quantity, delivery_dates, capacity, weight, attr, original_orders, merged_id):
self.product_id = product_id # 产品号
self.total_quantity = total_quantity # 合并后总数量
self.delivery_dates = delivery_dates # 原始订单发货日期列表
self.capacity = capacity # 产能
self.weight = weight # 产品重量
self.processing_hours = total_quantity / capacity # 总生产工时
self.attr = attr # 产品属性
self.original_orders = original_orders # 原始订单对象列表
self.start_time = None # 开始时间
self.end_time = None # 结束时间
self.machine = None # 分配机台
self.merged_id = merged_id # 合单序号
self.plan_number = None # 计划序号
class Machine:
"""机台类:封装机台属性与状态"""
def __init__(self, machine_id, initial_time=None):
self.machine_id = machine_id # 机台号
# 设置初始可用时间
if initial_time is None:
self.available_time = datetime(2025, 3, 1)
else:
self.available_time = initial_time
self.last_product = None # 上一生产产品
self.adjacent = [] # 相邻机台列表
self.schedule = [] # 排程计划(任务列表)
self.last_plan_number = None # 上一个任务的计划序号
def load_official_data():
"""加载正式数据(使用相对路径)"""
try:
# 1. 读取附件1(1)数据
attachment1 = pd.ExcelFile(r"C:\Users\彭婉\Desktop\2025-08\附件1(1).xlsx")
# 1.1 订单表
orders_df = attachment1.parse(
sheet_name="订单表",
parse_dates=["发货日期(DeliveryDate)"]
)
# 1.2 机台初始工作状态表
machine_initial_df = attachment1.parse(
sheet_name="机台初始工作状态表",
parse_dates=["生产开始时间"]
)
machine_initial_times = {
row["机台号"]: row["生产开始时间"]
for _, row in machine_initial_df.iterrows()
}
# 1.3 放假日期表
holidays_df = attachment1.parse(sheet_name="放假日期表")
holidays = {pd.to_datetime(row["放假日期"]).date() for _, row in holidays_df.iterrows()}
# 2. 读取附件2(1)数据
attachment2 = pd.ExcelFile(r"C:\Users\彭婉\Desktop\2025-08\附件2(1).xlsx")
# 2.1 产品工时计算参数表
product_capacity_df = attachment2.parse(sheet_name="产品工时计算参数表")
product_capacity = {
row["产品号"]: row["Capacity(pcs/h)"]
for _, row in product_capacity_df.iterrows()
}
product_weight = {
row["产品号"]: row["Weight/pcs(g)"]
for _, row in product_capacity_df.iterrows()
}
# 2.2 产品机台生产关系表
product_machine_df = attachment2.parse(sheet_name="产品机台生产关系表")
product_machines = {}
for _, row in product_machine_df.iterrows():
product_id = row["产品号"]
# 找出值为1的机台列
valid_machines = [col for col in product_machine_df.columns if col != "产品号" and row[col] == 1]
product_machines[product_id] = valid_machines
# 2.3 机台关系表
machine_relation_df = attachment2.parse(sheet_name="机台关系表")
machine_adjacent = {}
for _, row in machine_relation_df.iterrows():
machine_id = row["机台号"]
# 找出值为1的相邻机台列
adjacent_machines = [col for col in machine_relation_df.columns if col != "机台号" and row[col] == 1]
machine_adjacent[machine_id] = adjacent_machines
# 2.4 产品属性表
product_attr_df = attachment2.parse(sheet_name="产品属性表")
product_attrs = {
row["产品号"]: row["属性"]
for _, row in product_attr_df.iterrows()
}
# 3. 初始化订单数据(补充产能列)
orders_df["Capacity(pcs/h)"] = orders_df["产品号"].map(product_capacity)
orders_df["Weight/pcs(g)"] = orders_df["产品号"].map(product_weight)
# 4. 初始化机台对象
machines = []
for mid in machine_initial_times.keys():
# 确保每个机台都有初始时间
initial_time = machine_initial_times.get(mid, datetime(2025, 3, 1))
machine = Machine(mid, initial_time)
# 设置相邻机台关系
machine.adjacent = machine_adjacent.get(mid, [])
machines.append(machine)
return orders_df, product_machines, product_attrs, machines, holidays
except Exception as e:
print(f"加载数据错误: {e}")
# 返回空数据集避免崩溃
return pd.DataFrame(), {}, {}, [], set()
def merge_orders(orders, merge_days):
"""合单逻辑:相同产品号的订单,发货日期在merge_days内的合并为一个生产任务"""
merged_tasks = []
merge_count = 0 # 合单次数(合并的组数)
merged_id_counter = 1 # 合单序号计数器
# 按产品号分组
product_groups = {}
for order in orders:
if order.product_id not in product_groups:
product_groups[order.product_id] = []
product_groups[order.product_id].append(order)
# 对每个产品组内的订单按发货日期排序并合单
for product_id, group_orders in product_groups.items():
# 按发货日期升序排序
group_orders.sort(key=lambda x: x.delivery_date)
if len(group_orders) == 1:
# 单个订单无需合单,直接作为一个任务
merged_order = MergedOrder(
product_id=product_id,
total_quantity=group_orders[0].quantity,
delivery_dates=[group_orders[0].delivery_date],
capacity=group_orders[0].capacity,
weight=group_orders[0].weight,
attr=group_orders[0].attr,
original_orders=[group_orders[0]],
merged_id=merged_id_counter
)
merged_tasks.append(merged_order)
merged_id_counter += 1
continue
# 合并逻辑:滑动窗口检查发货日期差
current_merge = [group_orders[0]]
for i in range(1, len(group_orders)):
# 计算当前订单与组内第一个订单的发货日期差
date_diff = (group_orders[i].delivery_date - current_merge[0].delivery_date).days
if date_diff <= merge_days:
current_merge.append(group_orders[i])
else:
# 超过合单窗口,生成合并任务
merged_order = MergedOrder(
product_id=product_id,
total_quantity=sum(o.quantity for o in current_merge),
delivery_dates=[o.delivery_date for o in current_merge],
capacity=current_merge[0].capacity,
weight=current_merge[0].weight,
attr=current_merge[0].attr,
original_orders=current_merge,
merged_id=merged_id_counter
)
merged_tasks.append(merged_order)
merge_count += 1 # 记录一次合单
merged_id_counter += 1
current_merge = [group_orders[i]] # 开始新的一组
# 处理最后一组合单
if len(current_merge) >= 1:
merged_order = MergedOrder(
product_id=product_id,
total_quantity=sum(o.quantity for o in current_merge),
delivery_dates=[o.delivery_date for o in current_merge],
capacity=current_merge[0].capacity,
weight=current_merge[0].weight,
attr=current_merge[0].attr,
original_orders=current_merge,
merged_id=merged_id_counter
)
merged_tasks.append(merged_order)
merged_id_counter += 1
if len(current_merge) > 1:
merge_count += 1 # 仅多订单合并才计数
return merged_tasks, merge_count
def calculate_end_time(start_time, processing_hours, holidays):
"""计算考虑休息和放假后的生产结束时间"""
current = start_time
remaining = processing_hours
while remaining > 0:
# 跳过放假日期
if current.date() in holidays:
# 直接跳到第二天0点
current = datetime.combine(current.date() + timedelta(days=1), time(0, 0))
continue
# 计算当前时间点
current_time = current.time()
current_date = current.date()
day_start = datetime.combine(current_date, time(0, 0))
# 计算当天的工作时间段
work_periods = []
last_end = day_start
# 根据休息时段分割工作时间段
for rest_start, rest_end in REST_PERIODS:
rest_start_time = day_start + timedelta(hours=rest_start)
rest_end_time = day_start + timedelta(hours=rest_end)
# 添加休息前的工作时间段
if last_end < rest_start_time:
work_periods.append((last_end, rest_start_time))
last_end = rest_end_time
# 添加休息后的工作时间段
if last_end < day_start + timedelta(hours=24):
work_periods.append((last_end, day_start + timedelta(hours=24)))
# 处理每个工作时间段
for period_start, period_end in work_periods:
if current >= period_end:
continue # 当前时间段已结束
if current < period_start:
current = period_start # 跳到时间段开始
# 计算当前时间段可用时长
available_seconds = (period_end - current).total_seconds()
available_hours = available_seconds / 3600
# 如果剩余时间小于可用时长
if remaining <= available_hours:
current += timedelta(hours=remaining)
remaining = 0
return current
else:
# 消耗整个时间段
current = period_end
remaining -= available_hours
# 进入下一天
current = datetime.combine(current.date() + timedelta(days=1), time(0, 0))
return current
def problem3_scheduling(merged_tasks, product_machines, product_attrs, machines, holidays):
"""问题三排程:考虑产品属性、机台关系及合单"""
# 排序:优先易搭配>难生产>无限制,同属性按最早发货日期排序
merged_tasks.sort(key=lambda x: (
x.attr, # 1:易搭配优先,3:难生产其次,2:无限制最后
min(x.delivery_dates) # 按最早发货日期排序
))
# 机台映射表(便于查询相邻机台)
machine_map = {m.machine_id: m for m in machines}
plan_number = 1 # 计划序号计数器
detailed_results = [] # 存储详细排程结果
for task in merged_tasks:
# 筛选可生产该产品的机台
candidate_machines = [
m for m in machines
if m.machine_id in product_machines.get(task.product_id, [])
]
# 易搭配产品优先分配M03机台
if task.attr == 1:
m03 = next((m for m in candidate_machines if m.machine_id == "M03"), None)
if m03:
candidate_machines = [m03] + [m for m in candidate_machines if m.machine_id != "M03"]
best_machine = None
best_end = None
best_start = None
for machine in candidate_machines:
# 计算换模时间(首单或不同产品需换模)
change = CHANGE_TIME if machine.last_product != task.product_id else 0
initial_start = machine.available_time + timedelta(hours=change)
end_time = calculate_end_time(initial_start, task.processing_hours, holidays)
adjusted_start = initial_start
# 难生产产品约束:相邻机台不能同时生产难生产产品
if task.attr == 3:
for adj_id in machine.adjacent:
adj_machine = machine_map.get(adj_id)
if not adj_machine:
continue
# 检查相邻机台当前任务(如果有)
if adj_machine.schedule:
last_task = adj_machine.schedule[-1]
# 如果相邻机台正在生产难生产产品且时间冲突
if last_task["product_attr"] == 3 and not (
end_time <= last_task["start"] or adjusted_start >= last_task["end"]
):
# 调整开始时间至冲突任务结束后
adjusted_start = max(adjusted_start, last_task["end"])
end_time = calculate_end_time(adjusted_start, task.processing_hours, holidays)
# 更新最优机台(最早结束时间)
if best_end is None or end_time < best_end:
best_end = end_time
best_start = adjusted_start
best_machine = machine
# 分配机台并更新状态
if best_machine:
task.start_time = best_start
task.end_time = best_end
task.machine = best_machine.machine_id
task.plan_number = plan_number
# 更新原始订单的延期和满意度
for original_order in task.original_orders:
original_order.start_time = best_start
original_order.end_time = best_end
original_order.machine = best_machine.machine_id
original_order.merged_id = task.merged_id
original_order.plan_number = plan_number
original_order.forward_plan = best_machine.last_plan_number
original_order.delay_days = max(0, (best_end.date() - original_order.delivery_date.date()).days)
# 按文档公式计算满意度
original_order.satisfaction = max(0, 10.0 - (original_order.delay_days // 3) * 0.1)
# 计算最迟开始时间
late_start_time = original_order.delivery_date - timedelta(hours=original_order.processing_hours)
# 收集详细排程信息
detailed_results.append({
"订单号PO": original_order.order_id,
"产品号": original_order.product_id,
"需要工时 (Requested Time--Hours)": round(original_order.processing_hours, 2),
"生产计划开始时间": original_order.start_time.strftime("%Y-%m-%d %H:%M:%S"),
"生产计划预计完成时间": original_order.end_time.strftime("%Y-%m-%d %H:%M:%S"),
"发货日期(DeliveryDate)": original_order.delivery_date.strftime("%Y-%m-%d"),
"订单数量": original_order.quantity,
"Weight/pcs(g)": original_order.weight,
"Capacity(pcs/h)": original_order.capacity,
"最迟开始时间 (Late Start Time)": late_start_time.strftime("%Y-%m-%d %H:%M:%S"),
"合单序号(Joined Order Number)": original_order.merged_id,
"前置任务生产计划序号": original_order.forward_plan or "",
"是否延期": "是" if original_order.delay_days > 0 else "否",
"延期天数": original_order.delay_days,
"计划序号(Plan Number)": plan_number,
"生产计划安排机台号": best_machine.machine_id
})
# 更新机台状态
best_machine.available_time = best_end
best_machine.last_product = task.product_id
best_machine.last_plan_number = plan_number
best_machine.schedule.append({
"product": task.product_id,
"start": best_start,
"end": best_end,
"product_attr": task.attr,
"original_orders": [o.order_id for o in task.original_orders],
"plan_number": plan_number
})
plan_number += 1
# 收集所有原始订单用于统计
all_original_orders = []
for task in merged_tasks:
all_original_orders.extend(task.original_orders)
# 统计结果
delay_orders = [o for o in all_original_orders if o.delay_days > 0]
max_delay = max([o.delay_days for o in delay_orders]) if delay_orders else 0
avg_delay = round(np.mean([o.delay_days for o in delay_orders]) if delay_orders else 0, 1)
avg_satisfaction = round(np.mean([o.satisfaction for o in all_original_orders]), 2)
min_satisfaction = round(min([o.satisfaction for o in all_original_orders]), 2)
return detailed_results, {
"延期订单总数": len(delay_orders),
"最长延期天数": max_delay,
"平均延期天数": avg_delay,
"订单平均满意度": avg_satisfaction,
"订单最差满意度": min_satisfaction
}
def export_problem3_results(results, output_path=r"C:\Users\彭婉\Desktop\2025-08\工作簿1.xlsx"):
"""导出问题三结果到Excel(按指定格式)"""
# 创建结果DataFrame
result_df = pd.DataFrame(results)
# 重命名列以符合要求
result_df.rename(columns={
"合单类别": "合单类别",
"合单次数": "合单次数",
"延期订单总数": "延期订单总数",
"最长延期天数": "最长前期天数", # 注意这里按要求改为"最长前期天数"
"平均延期天数": "平均延期天数",
"订单平均满意度": "订单平均满意度",
"订单最差满意度": "订单最差满意度"
}, inplace=True)
# 设置列顺序
column_order = [
"合单类别", "合单次数", "延期订单总数",
"最长前期天数", "平均延期天数",
"订单平均满意度", "订单最差满意度"
]
result_df = result_df[column_order]
# 导出到Excel
with pd.ExcelWriter(output_path, engine="openpyxl") as writer:
result_df.to_excel(writer, sheet_name="生产排程结果", index=False)
print(f"结果已导出至:{os.path.abspath(output_path)}")
return result_df
def export_detailed_schedule(detailed_results, output_path, sheet_name):
"""导出详细排程结果到Excel"""
if not detailed_results:
print(f"警告: {sheet_name} 没有详细排程数据可导出")
return
# 创建DataFrame
df = pd.DataFrame(detailed_results)
# 设置列顺序
column_order = [
"计划序号(Plan Number)",
"生产计划安排机台号",
"订单号PO",
"产品号",
"需要工时 (Requested Time--Hours)",
"生产计划开始时间",
"生产计划预计完成时间",
"发货日期(DeliveryDate)",
"订单数量",
"Weight/pcs(g)",
"Capacity(pcs/h)",
"最迟开始时间 (Late Start Time)",
"合单序号(Joined Order Number)",
"前置任务生产计划序号",
"是否延期",
"延期天数"
]
# 确保只包含需要的列
df = df[column_order]
# 导出到Excel
with pd.ExcelWriter(output_path, engine="openpyxl", mode='a' if os.path.exists(output_path) else 'w') as writer:
df.to_excel(writer, sheet_name=sheet_name, index=False)
print(f"详细排程结果已导出至: {output_path} (工作表: {sheet_name})")
def main():
# 加载数据
orders_df, product_machines, product_attrs, original_machines, holidays = load_official_data()
# 检查数据是否加载成功
if orders_df.empty or not original_machines:
print("数据加载失败,请检查文件路径和格式")
return
# 初始化原始订单对象
original_orders = []
for _, row in orders_df.iterrows():
try:
order = Order(
row["订单号"],
row["产品号"],
row["订单数量"],
row["发货日期(DeliveryDate)"],
row["Capacity(pcs/h)"],
row["Weight/pcs(g)"],
product_attrs.get(row["产品号"], 2) # 默认属性为2(无限制)
)
original_orders.append(order)
except Exception as e:
print(f"订单初始化错误: {e} - 行数据: {row}")
# 问题三:分别处理7天、15天、30天合单
merge_days_list = [7, 15, 30]
problem3_results = []
detailed_output_path = r"C:\Users\彭婉\Desktop\2025-08\详细排程结果.xlsx"
# 如果输出文件已存在,先删除
if os.path.exists(detailed_output_path):
os.remove(detailed_output_path)
for merge_days in merge_days_list:
print(f"\n===== 处理 {merge_days} 天合单 =====")
# 复制机台(避免状态污染)
machines = []
for m in original_machines:
# 正确复制机台初始时间
new_machine = Machine(m.machine_id, m.available_time)
new_machine.adjacent = m.adjacent.copy()
new_machine.last_product = m.last_product
new_machine.last_plan_number = m.last_plan_number
machines.append(new_machine)
# 1. 合单
merged_tasks, merge_count = merge_orders(original_orders.copy(), merge_days)
print(f"合单次数:{merge_count}")
# 2. 排程
try:
detailed_results, stats = problem3_scheduling(
merged_tasks, product_machines, product_attrs, machines, holidays
)
except Exception as e:
print(f"排程错误: {e}")
continue
# 3. 导出详细排程结果
sheet_name = f"{merge_days}天合单排程"
export_detailed_schedule(detailed_results, detailed_output_path, sheet_name)
# 4. 收集统计结果
problem3_results.append({
"合单类别": f"{merge_days}天合单",
"合单次数": merge_count,
"延期订单总数": stats["延期订单总数"],
"最长延期天数": stats["最长延期天数"],
"平均延期天数": stats["平均延期天数"],
"订单平均满意度": stats["订单平均满意度"],
"订单最差满意度": stats["订单最差满意度"]
})
# 5. 导出统计结果
print("\n===== 问题三最终结果 =====")
result_df = export_problem3_results(problem3_results)
# 控制台输出表格
print("\n生产排程结果表:")
print(result_df.to_string(index=False))
if __name__ == "__main__":
main()
```
### 主要修改和新增功能:
1. **详细排程输出功能**:
- 新增了`export_detailed_schedule`函数,用于导出详细排程结果
- 每次排程后会生成包含以下16列的详细排程表:
1. 计划序号(Plan Number)
2. 生产计划安排机台号
3. 订单号PO
4. 产品号
5. 需要工时 (Requested Time--Hours)
6. 生产计划开始时间
7. 生产计划预计完成时间
8. 发货日期(DeliveryDate)
9. 订单数量
10. Weight/pcs(g)
11. Capacity(pcs/h)
12. 最迟开始时间 (Late Start Time)
13. 合单序号(Joined Order Number)
14. 前置任务生产计划序号
15. 是否延期
16. 延期天数
2. **合单逻辑优化**:
- 为每个合并任务添加了唯一的`merged_id`标识符
- 确保每个订单都有正确的合单序号关联
3. **排程逻辑增强**:
- 添加了计划序号计数器`plan_number`
- 跟踪每个任务的前置任务计划序号
- 计算并存储每个订单的最迟开始时间
4. **结果导出机制**:
- 为每种合单策略(7天、15天、30天)创建单独的工作表
- 所有详细排程结果保存在同一个Excel文件中
- 保留原有的统计结果导出功能
5. **时间计算优化**:
- 改进了`calculate_end_time`函数,更精确地处理休息时段
- 使用时间段分割代替小时级循环,提高计算效率
### 输出说明:
1. **详细排程文件**:
- 路径:`C:\Users\彭婉\Desktop\2025-08\详细排程结果.xlsx`
- 包含三个工作表:`7天合单排程`、`15天合单排程`、`30天合单排程`
- 每个工作表包含所有订单的详细排程信息(16列)
2. **统计结果文件**:
- 路径:`C:\Users\彭婉\Desktop\2025-08\工作簿1.xlsx`
- 包含每种合单策略的统计指标:
- 合单类别
- 合单次数
- 延期订单总数
- 最长前期天数
- 平均延期天数
- 订单平均满意度
- 订单最差满意度
3. **控制台输出**:
- 显示处理进度和关键统计指标
- 最后输出统计结果表格
### 使用说明:
1. 确保附件路径正确(默认为`C:\Users\彭婉\Desktop\2025-08\`)
2. 运行程序后,会在指定路径生成两个Excel文件:
- `详细排程结果.xlsx`:包含每种合单策略的详细排程
- `工作簿1.xlsx`:包含每种合单策略的统计结果
3. 程序会在控制台输出处理进度和最终统计结果