from datetime import datetime, timedelta
from typing import List, Dict, Optional
from enum import Enum
import heapq
import logging
# 配置日志
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
class SchedulingStrategy(Enum):
"""排程策略枚举类"""
DUE_DATE = "due_date" # 按交期排程
PRIORITY = "priority" # 按优先级排程
SHORTEST_FIRST = "shortest" # 最短工期优先
LONGEST_FIRST = "longest" # 最长工期优先
EDD = "edd" # 最早交期优先
SPT = "spt" # 最短加工时间优先
def validate_time_range(start_time: datetime, end_time: datetime) -> bool:
"""验证时间范围的有效性"""
if start_time >= end_time:
return False
if (end_time - start_time).total_seconds() / 3600 > 24: # 限制单工序最长24小时
return False
return True
class Operator:
"""操作员类"""
def __init__(self, operator_id: str, name: str, skills: List[str], shift: str = "day"):
self.operator_id = operator_id # 操作员ID
self.name = name # 操作员名称
self.skills = skills # 操作员拥有的技能列表
# 确保班次为 'day' 或 'night'
if shift not in ["day", "night"]:
raise ValueError("班次必须是 'day' 或 'night'")
self.shift = shift # 操作员的班次
self.schedule = [] # 操作员的工作排班
self.available_time = datetime.now() # 操作员下次可用的时间
self.total_working_hours = 0 # 操作员的总工作时长
def is_qualified(self, equipment_type: str) -> bool:
"""检查操作员是否具备特定设备的操作资格"""
# 如果操作员的技能列表包含该设备类型,则返回 True
return equipment_type in self.skills
def is_available(self, start_time: datetime, end_time: datetime) -> bool:
"""检查操作员在指定时间段是否可用"""
# 先检查时间范围的有效性
if not validate_time_range(start_time, end_time):
return False
# 检查操作员的班次
if self.shift == "day":
return True # 临时简化班次检查,先确保基本功能正常
elif self.shift == "night":
return True
# 检查操作员的现有工作排班
for work in self.schedule:
# 如果新的工作时间段与现有排班重叠,则该时间段不可用
if (start_time < work['end_time'] and end_time > work['start_time']):
return False
# 该时间段可用
return True
def schedule_work(self, work_order, start_time: datetime, end_time: datetime):
"""安排工作"""
# 先检查操作员在该时间段是否可用
if not self.is_available(start_time, end_time):
raise ValueError(f"操作员 {self.name} 在指定时间段不可用")
# 更新操作员的工作排班
self.schedule.append({
'work_order': work_order,
'start_time': start_time,
'end_time': end_time
})
# 更新操作员的下次可用时间
self.available_time = max(self.available_time, end_time)
# 更新操作员的总工作时长
self.total_working_hours += (end_time - start_time).total_seconds() / 3600
logger.info(f"操作员 {self.name} 被安排工作: {work_order.order_id}")
class Equipment:
def __init__(self, equipment_id: str, name: str, type: str, precision: float,
max_size: Dict[str, float], maintenance_cycle: int):
self.equipment_id = equipment_id # 设备 ID
self.name = name # 设备名称
self.type = type # 设备类型
self.precision = precision # 设备精度
self.max_size = max_size # 设备最大尺寸
self.maintenance_cycle = maintenance_cycle # 设备维护周期
self.last_maintenance = datetime.now() # 上次维护时间
self.status = "idle" # 设备状态,可能的值有 "idle"、"working"
self.total_working_hours = 0.0 # 设备总工作时长
self.current_work_start = None # 当前工作的开始时间
self.work_records = [] # 记录所有工作
logger.info(f"创建设备: {self.name}")
def start_work(self, start_time: datetime) -> bool:
"""开始工作"""
# 如果设备当前不处于空闲状态,则无法开始新工作
if self.status != "idle":
logger.debug(f"设备 {self.name} 当前状态 {self.status},无法开始新工作")
return False
# 设置设备状态为 "working",并记录工作开始时间
self.status = "working"
self.current_work_start = start_time
logger.debug(f"设备 {self.name} 开始工作,时间: {start_time}")
return True
def end_work(self, end_time: datetime):
"""结束工作"""
# 如果设备当前不处于工作状态,或者没有记录当前工作的开始时间,则无法结束工作
if self.status != "working" or not self.current_work_start:
logger.warning(f"设备 {self.name} 无法结束工作:当前状态 {self.status}")
return
# 计算工作持续时长,并更新设备的总工作时长
work_duration = (end_time - self.current_work_start).total_seconds() / 3600
self.total_working_hours += work_duration
# 记录此次工作的详细信息
self.work_records.append({
'start_time': self.current_work_start,
'end_time': end_time,
'duration': work_duration
})
# 将设备状态设置为 "idle",并清空当前工作的开始时间
self.status = "idle"
self.current_work_start = None
logger.debug(f"设备 {self.name} 结束工作,时间: {end_time}, 持续: {work_duration:.2f}小时")
def get_utilization_rate(self, start_time: datetime, end_time: datetime) -> float:
"""计算设备在给定时间段内的利用率"""
# 如果没有任何工作记录,则利用率为 0
if not self.work_records:
return 0.0
# 计算给定时间段的总时长
total_duration = (end_time - start_time).total_seconds() / 3600 # 转换为小时
if total_duration <= 0:
return 0.0
# 计算设备在给定时间段内的工作时长
working_hours = sum(record['duration'] for record in self.work_records)
utilization = (working_hours / total_duration) * 100
# 确保利用率在 0-100% 之间
return min(100.0, max(0.0, utilization))
class WorkStation:
"""工作站类"""
def __init__(self, station_id: str, name: str, capacity: float = 1.0):
# 验证产能系数是否大于0
if capacity <= 0:
raise ValueError("产能系数必须大于0")
self.station_id = station_id # 工作站 ID
self.name = name # 工作站名称
self.capacity = capacity # 产能系数
self.schedule = [] # 工作排程记录
self.available_time = datetime.now() # 下次可用的时间
self.equipment = None # 分配的设备
self.operators = [] # 分配的操作员列表
self.required_skills = [] # 所需的操作员技能列表
self.status = "idle" # 工作站的当前状态
def validate_capacity(self):
"""验证工作站能力"""
# 检查是否分配了设备
if not self.equipment:
raise ValueError(f"工作站 {self.station_id} 未分配设备")
# 检查是否分配了操作员
if not self.operators:
raise ValueError(f"工作站 {self.station_id} 未分配操作员")
# 检查是否定义了所需技能
if not self.required_skills:
raise ValueError(f"工作站 {self.station_id} 未定义所需技能")
def assign_equipment(self, equipment: Equipment):
"""分配设备到工作站"""
self.equipment = equipment
logger.info(f"设备 {equipment.name} 被分配到工作站 {self.name}")
def assign_operator(self, operator: Operator):
"""分配操作员到工作站"""
# 如果工作站未定义所需技能,无法分配操作员
if not self.required_skills:
raise ValueError(f"工作站 {self.station_id} 未定义所需技能")
# 检查操作员是否具备所需技能
if any(skill in operator.skills for skill in self.required_skills):
# 如果操作员尚未分配到该工作站,则进行分配
if operator not in self.operators:
self.operators.append(operator)
logger.info(f"操作员 {operator.name} 被分配到工作站 {self.name}")
return True
# 如果操作员不满足要求,则分配失败
return False
def get_available_operator(self, start_time: datetime, end_time: datetime) -> Optional[Operator]:
"""获取指定时间段可用的操作员"""
# 先验证时间范围的有效性
if not validate_time_range(start_time, end_time):
return None
# 遍历所有分配到该工作站的操作员,找到在指定时间段内可用的操作员
for operator in self.operators:
if operator.is_available(start_time, end_time):
return operator
# 如果没有找到可用的操作员,返回 None
return None
def can_process(self, work_order) -> bool:
"""检查工作站是否能够处理特定工单"""
try:
# 基础检查:检查是否分配了设备,以及设备是否处于空闲状态
if not self.equipment:
logger.debug(f"工作站 {self.name} 未分配设备")
return False
if self.equipment.status != "idle":
logger.debug(f"工作站 {self.name} 设备状态不是空闲")
return False
# 暂时放宽设备和操作员的技能匹配检查
return True
except Exception as e:
logger.error(f"工作站 {self.name} 能力检查出错: {str(e)}")
return False
def schedule_work(self, work_order, start_time: datetime, end_time: datetime) -> bool:
"""在工作站安排工作"""
# 验证时间范围的有效性
if not validate_time_range(start_time, end_time):
logger.debug(f"工作站 {self.name} 时间范围无效")
return False
logger.debug(f"工作站 {self.name} 尝试安排工作: {start_time} -> {end_time}")
try:
# 启动设备
if not self.equipment.start_work(start_time):
logger.debug(f"工作站 {self.name} 设备启动失败")
return False
# 找到可用的操作员
operator = self.get_available_operator(start_time, end_time)
if not operator:
logger.debug(f"工作站 {self.name} 未找到可用操作员")
self.equipment.status = "idle"
return False
# 记录工作安排
self.schedule.append({
'work_order': work_order,
'start_time': start_time,
'end_time': end_time,
'operator': operator
})
# 更新操作员排程
operator.schedule_work(work_order, start_time, end_time)
# 结束设备工作并记录时间
self.equipment.end_work(end_time)
self.available_time = max(self.available_time, end_time)
logger.info(f"工作站 {self.name} 成功安排工单 {work_order.order_id}")
return True
except Exception as e:
logger.error(f"工作站 {self.name} 安排工作失败: {str(e)}")
# 清理失败的安排
if self.equipment.status == "working":
self.equipment.status = "idle"
if len(self.schedule) > 0 and self.schedule[-1]['work_order'] == work_order:
self.schedule.pop()
return False
class WorkOrder:
"""工单类"""
def __init__(self,
order_id: str,
product_id: str,
quantity: int,
due_date: datetime,
priority: int = 1,
routing: List[Dict] = None,
precision_requirements: Dict[str, float] = None,
size_requirements: Dict[str, Dict[str, float]] = None):
# 对输入数据进行验证
self._validate_input(order_id, product_id, quantity, due_date, priority, routing)
self.order_id = order_id # 工单编号
self.product_id = product_id # 产品编号
self.quantity = quantity # 生产数量
self.due_date = due_date # 交货期
self.priority = priority # 优先级
self.routing = routing or [] # 工艺路线
self.status = "pending" # 工单状态,可能的值有 "pending"、"scheduled"、"in_progress"、"completed"、"cancelled"
self.actual_start_time = None # 实际开始时间
self.actual_end_time = None # 实际完成时间
self.scheduled_start_time = None # 计划开始时间
self.scheduled_end_time = None # 计划完成时间
self.precision_requirements = precision_requirements or {} # 精度要求
self.size_requirements = size_requirements or {} # 尺寸要求
self.assigned_operators = {} # 分配的操作员
self.schedule = [] # 工作安排
self.available_time = None # 下次可用的时间
logger.info(f"创建工单: {self.order_id}, 产品: {self.product_id}, 数量: {self.quantity}")
def schedule_work(self, work_order, start_time: datetime, end_time: datetime):
"""在工作站安排工作"""
self.schedule.append({
'work_order': work_order,
'start_time': start_time,
'end_time': end_time
})
self.available_time = max(self.available_time, end_time)
def _validate_input(self, order_id: str, product_id: str, quantity: int,
due_date: datetime, priority: int, routing: List[Dict]):
"""验证输入数据"""
# 检查工单号和产品编号是否为空
if not order_id or not product_id:
raise ValueError("工单号和产品编号不能为空")
# 检查数量是否大于0
if quantity <= 0:
raise ValueError("数量必须大于0")
# 检查交期是否晚于当前时间
if due_date <= datetime.now():
raise ValueError("交期必须晚于当前时间")
# 检查优先级是否大于0
if priority <= 0:
raise ValueError("优先级必须大于0")
# 检查工艺路线数据是否完整
if routing:
for step in routing:
if 'station_id' not in step or 'std_time' not in step:
raise ValueError("工艺路线数据不完整")
if step['std_time'] <= 0:
raise ValueError("标准工时必须大于0")
def get_total_processing_time(self) -> float:
"""计算总加工时间"""
return sum(step['std_time'] * self.quantity for step in self.routing)
def is_delayed(self) -> bool:
"""检查是否延期"""
if self.scheduled_end_time:
return self.scheduled_end_time > self.due_date
return False
def update_status(self, new_status: str):
"""更新工单状态"""
# 定义有效的工单状态
valid_statuses = ["pending", "scheduled", "in_progress", "completed", "cancelled"]
# 检查新状态是否有效
if new_status not in valid_statuses:
raise ValueError(f"无效的状态: {new_status}")
self.status = new_status
logger.info(f"工单 {self.order_id} 状态更新为: {new_status}")
class ProductionScheduler:
"""生产排程器"""
def __init__(self):
self.work_stations = {} # 所有工作站
self.work_orders = [] # 所有工单
self.scheduled_orders = [] # 已排程的工单
self.current_time = datetime.now() # 当前时间
self.operators = [] # 所有操作员
self.equipment_list = [] # 所有设备
logger.info("初始化生产排程器")
def get_work_station(self, station_id: str) -> Optional[WorkStation]:
"""获取工作站"""
return self.work_stations.get(station_id)
def schedule_by_strategy(self, strategy: SchedulingStrategy) -> List[Dict]:
"""根据指定策略进行排程"""
logger.info(f"使用策略 {strategy.value} 开始排程")
# 清空之前的排程结果,但保留原始工单列表
self.scheduled_orders = []
# 获取排序后的工单
sorted_orders = self.sort_orders_by_strategy(strategy)
logger.info(f"待排程工单数量: {len(sorted_orders)}")
current_time = datetime.now()
schedule_results = []
for order in self.work_orders: # 使用原始工单列表
logger.info(f"\n开始排程工单 {order.order_id}")
logger.info(f"工序数量: {len(order.routing)}")
success = True
order_start_time = current_time
# 遍历工序
for op_index, operation in enumerate(order.routing):
station_id = operation['station_id']
std_time = operation['std_time']
total_time = std_time * order.quantity
logger.info(f"排程工序 {op_index + 1}: 工作站={station_id}, "
f"标准时间={std_time}h, 总时间={total_time}h")
# 获取工作站
station = self.get_work_station(station_id)
if not station:
logger.error(f"找不到工作站 {station_id}")
success = False
break
# 检查设备可用性
if not station.equipment:
logger.error(f"工作站 {station_id} 没有分配设备")
success = False
break
# 尝试安排工序
end_time = current_time + timedelta(hours=total_time)
if not station.schedule_work(order, current_time, end_time):
logger.error(f"工序安排失败: 工单={order.order_id}, "
f"工作站={station_id}")
success = False
break
current_time = end_time
if success:
order.scheduled_start_time = order_start_time
order.scheduled_end_time = current_time
self.scheduled_orders.append(order)
schedule_results.append({
'order_id': order.order_id,
'start_time': order_start_time,
'end_time': current_time,
'total_time': (current_time - order_start_time).total_seconds() / 3600
})
logger.info(f"工单 {order.order_id} 排程成功")
else:
logger.warning(f"工单 {order.order_id} 排程失败")
logger.info(f"完成排程,成功数量: {len(self.scheduled_orders)}")
return schedule_results
def sort_orders_by_strategy(self, strategy: SchedulingStrategy) -> List[WorkOrder]:
"""根据策略对工单进行排序"""
if strategy == SchedulingStrategy.DUE_DATE:
return sorted(self.work_orders, key=lambda x: x.due_date)
elif strategy == SchedulingStrategy.PRIORITY:
return sorted(self.work_orders, key=lambda x: (-x.priority, x.due_date))
elif strategy == SchedulingStrategy.SHORTEST_FIRST:
return sorted(self.work_orders, key=lambda x: x.get_total_processing_time())
elif strategy == SchedulingStrategy.LONGEST_FIRST:
return sorted(self.work_orders, key=lambda x: -x.get_total_processing_time())
elif strategy == SchedulingStrategy.EDD:
return sorted(self.work_orders, key=lambda x: x.due_date)
elif strategy == SchedulingStrategy.SPT:
return sorted(self.work_orders, key=lambda x: x.get_total_processing_time())
else:
return self.work_orders.copy()
def validate_system(self):
"""验证系统配置"""
if not self.work_stations:
raise ValueError("未配置工作站")
if not self.operators:
raise ValueError("未配置操作员")
if not self.equipment_list:
raise ValueError("未配置设备")
# 验证每个工作站的配置
for station in self.work_stations.values():
try:
station.validate_capacity()
except ValueError as e:
logger.error(f"工作站验证失败: {str(e)}")
raise
def add_work_station(self, station: WorkStation):
"""添加工作站"""
if station.station_id in self.work_stations:
raise ValueError(f"工作站ID {station.station_id} 已存在")
self.work_stations[station.station_id] = station
logger.info(f"添加工作站: {station.name}")
def add_work_order(self, work_order: WorkOrder):
"""添加工单"""
# 验证工艺路线中的工作站是否存在
for step in work_order.routing:
if step['station_id'] not in self.work_stations:
raise ValueError(f"工艺路线中的工作站 {step['station_id']} 不存在")
self.work_orders.append(work_order)
logger.info(f"添加工单: {work_order.order_id}")
def add_operator(self, operator: Operator):
"""添加操作员"""
if any(op.operator_id == operator.operator_id for op in self.operators):
raise ValueError(f"操作员ID {operator.operator_id} 已存在")
self.operators.append(operator)
logger.info(f"添加操作员: {operator.name}")
def add_equipment(self, equipment: Equipment):
"""添加设备"""
if any(eq.equipment_id == equipment.equipment_id for eq in self.equipment_list):
raise ValueError(f"设备ID {equipment.equipment_id} 已存在")
self.equipment_list.append(equipment)
logger.info(f"添加设备: {equipment.name}")
def calculate_processing_time(self, quantity: int, std_time: float, capacity: float) -> float:
"""计算加工时间"""
if quantity <= 0 or std_time <= 0 or capacity <= 0:
raise ValueError("计算参数必须大于0")
return (quantity * std_time) / capacity
def _schedule_by_due_date(self):
"""按交期排程"""
sorted_orders = sorted(self.work_orders, key=lambda x: x.due_date)
return self._schedule_orders(sorted_orders)
def _schedule_by_priority(self):
"""按优先级排程"""
sorted_orders = sorted(self.work_orders, key=lambda x: (-x.priority, x.due_date))
return self._schedule_orders(sorted_orders)
def _schedule_shortest_first(self):
"""最短工期优先排程"""
orders_with_duration = [(order, order.get_total_processing_time())
for order in self.work_orders]
sorted_orders = [order for order, _ in sorted(orders_with_duration,
key=lambda x: x[1])]
return self._schedule_orders(sorted_orders)
def _schedule_longest_first(self):
"""最长工期优先排程"""
orders_with_duration = [(order, order.get_total_processing_time())
for order in self.work_orders]
sorted_orders = [order for order, _ in sorted(orders_with_duration,
key=lambda x: x[1],
reverse=True)]
return self._schedule_orders(sorted_orders)
def _schedule_earliest_due_date(self):
"""最早交期优先(EDD)排程"""
return self._schedule_by_due_date()
def _schedule_shortest_processing_time(self):
"""最短加工时间优先(SPT)排程"""
return self._schedule_shortest_first()
def _schedule_orders(self, sorted_orders: List[WorkOrder]):
"""执行实际的排程逻辑"""
schedule_results = []
for work_order in sorted_orders:
try:
order_schedule = self._schedule_single_order(work_order)
if order_schedule:
schedule_results.append(order_schedule)
work_order.status = "scheduled"
self.scheduled_orders.append(work_order)
logger.info(f"工单 {work_order.order_id} 排程成功")
else:
logger.warning(f"工单 {work_order.order_id} 排程失败")
except Exception as e:
logger.error(f"排程工单 {work_order.order_id} 时发生错误: {str(e)}")
continue
return schedule_results
def _schedule_single_order(self, work_order: WorkOrder) -> Optional[Dict]:
"""排程单个工单"""
current_time = self.current_time
schedule_steps = []
logger.debug(f"开始排程工单 {work_order.order_id}")
for step in work_order.routing:
station_id = step['station_id']
station = self.work_stations[station_id]
if not station.can_process(work_order):
logger.warning(f"工作站 {station_id} 无法处理工单 {work_order.order_id}")
continue
# 计算加工时间
processing_time = self.calculate_processing_time(
work_order.quantity,
step['std_time'],
station.capacity
)
# 确定开始时间
start_time = max(current_time, station.available_time)
end_time = start_time + timedelta(hours=processing_time)
logger.debug(f"尝试在工作站 {station_id} 安排工作: {start_time} -> {end_time}")
if station.schedule_work(work_order, start_time, end_time):
schedule_steps.append({
'station_id': station_id,
'start_time': start_time,
'end_time': end_time,
'processing_time': processing_time,
'operator': station.schedule[-1]['operator']
})
current_time = end_time
logger.info(f"工作站 {station_id} 成功安排工序,处理时间: {processing_time:.2f}小时")
else:
logger.warning(f"工作站 {station_id} 安排失败")
return None
if not schedule_steps:
return None
# 更新工单信息
work_order.scheduled_start_time = schedule_steps[0]['start_time']
work_order.scheduled_end_time = schedule_steps[-1]['end_time']
return {
'work_order': work_order,
'steps': schedule_steps,
'start_time': work_order.scheduled_start_time,
'end_time': work_order.scheduled_end_time
}
def get_schedule_statistics(self) -> Dict:
"""获取排程统计信息"""
# 创建基础统计信息
stats = {
'total_orders': 0,
'total_processing_time': 0.0,
'on_time_orders': 0,
'on_time_rate': 0.0,
'equipment_utilization': {eq.equipment_id: 0.0 for eq in self.equipment_list},
'operator_workload': {op.operator_id: 0.0 for op in self.operators},
'schedule_start': datetime.now(),
'schedule_end': datetime.now()
}
# 如果没有已排程的订单,直接返回基础统计信息
if not self.scheduled_orders:
logger.warning("没有已排程的工单")
return stats
try:
# 更新统计信息
stats['total_orders'] = len(self.scheduled_orders)
# 计算时间范围
valid_orders = [order for order in self.scheduled_orders
if order.scheduled_start_time and order.scheduled_end_time]
if valid_orders:
stats['schedule_start'] = min(order.scheduled_start_time
for order in valid_orders)
stats['schedule_end'] = max(order.scheduled_end_time
for order in valid_orders)
# 计算总加工时间
stats['total_processing_time'] = sum(
(order.scheduled_end_time - order.scheduled_start_time).total_seconds() / 3600
for order in valid_orders
)
# 计算准时完成的工单数
stats['on_time_orders'] = sum(1 for order in valid_orders
if not order.is_delayed())
# 计算准时率
stats['on_time_rate'] = (stats['on_time_orders'] / len(valid_orders) * 100
if valid_orders else 0)
# 计算设备利用率
for equipment in self.equipment_list:
utilization = equipment.get_utilization_rate(
stats['schedule_start'],
stats['schedule_end']
)
stats['equipment_utilization'][equipment.equipment_id] = utilization
logger.debug(f"设备 {equipment.name} 利用率: {utilization:.2f}%")
# 计算操作员工作负荷
for operator in self.operators:
stats['operator_workload'][operator.operator_id] = operator.total_working_hours
logger.debug(f"操作员 {operator.name} 工作时间: "
f"{operator.total_working_hours:.2f}小时")
except Exception as e:
logger.error(f"计算统计信息时出错: {str(e)}")
# 发生错误时返回基础统计信息
return stats
return stats
def main():
"""主函数:演示生产排程功能"""
try:
# 初始化排程器
scheduler = ProductionScheduler()
logger.info("开始生产排程演示")
# 创建设备
equipments = [
Equipment(
equipment_id="EQ001",
name="精密车床",
type="lathe",
precision=0.01,
max_size={"length": 1000, "diameter": 400},
maintenance_cycle=30
),
Equipment(
equipment_id="EQ002",
name="立式加工中心",
type="mill",
precision=0.005,
max_size={"x": 800, "y": 500, "z": 400},
maintenance_cycle=45
),
Equipment(
equipment_id="EQ003",
name="数控钻床",
type="drill",
precision=0.02,
max_size={"x": 600, "y": 400},
maintenance_cycle=60
)
]
# 创建操作员
operators = [
Operator(operator_id="OP001", name="张三",
skills=["lathe", "mill"], shift="day"),
Operator(operator_id="OP002", name="李四",
skills=["mill", "drill"], shift="day"),
Operator(operator_id="OP003", name="王五",
skills=["lathe", "drill"], shift="night")
]
# 创建工作站
stations = [
WorkStation('WS001', '车床工作站', 1.0),
WorkStation('WS002', '铣床工作站', 0.8),
WorkStation('WS003', '钻床工作站', 1.2)
]
# 配置工作站
stations[0].assign_equipment(equipments[0])
stations[0].required_skills = ["lathe"]
stations[0].assign_operator(operators[0])
stations[0].assign_operator(operators[2])
stations[1].assign_equipment(equipments[1])
stations[1].required_skills = ["mill"]
stations[1].assign_operator(operators[0])
stations[1].assign_operator(operators[1])
stations[2].assign_equipment(equipments[2])
stations[2].required_skills = ["drill"]
stations[2].assign_operator(operators[1])
stations[2].assign_operator(operators[2])
# 添加配置到排程器
for station in stations:
scheduler.add_work_station(station)
for operator in operators:
scheduler.add_operator(operator)
for equipment in equipments:
scheduler.add_equipment(equipment)
# 获取基准时间
base_date = datetime.now().replace(hour=8, minute=0, second=0, microsecond=0) # 从早上8点开始
# 创建工单
work_orders = [
WorkOrder(
order_id='WO001',
product_id='PROD001',
quantity=50, # 减少数量使时间更合理
due_date=base_date + timedelta(days=3),
priority=1,
routing=[
{'station_id': 'WS001', 'std_time': 0.2}, # 车床工序
{'station_id': 'WS002', 'std_time': 0.3}, # 铣床工序
{'station_id': 'WS003', 'std_time': 0.1} # 钻床工序
],
precision_requirements={
'WS001': 0.02,
'WS002': 0.01,
'WS003': 0.03
},
size_requirements={
'WS001': {'length': 500, 'diameter': 200},
'WS002': {'x': 400, 'y': 300, 'z': 200},
'WS003': {'x': 300, 'y': 200}
}
),
WorkOrder(
order_id='WO002',
product_id='PROD002',
quantity=30, # 减少数量
due_date=base_date + timedelta(days=2),
priority=2,
routing=[
{'station_id': 'WS001', 'std_time': 0.15}, # 车床工序
{'station_id': 'WS003', 'std_time': 0.25} # 钻床工序
],
precision_requirements={
'WS001': 0.02,
'WS003': 0.03
},
size_requirements={
'WS001': {'length': 400, 'diameter': 180},
'WS003': {'x': 280, 'y': 180}
}
),
WorkOrder(
order_id='WO003',
product_id='PROD003',
quantity=40, # 减少数量
due_date=base_date + timedelta(days=4),
priority=1,
routing=[
{'station_id': 'WS002', 'std_time': 0.3}, # 铣床工序
{'station_id': 'WS003', 'std_time': 0.2} # 钻床工序
],
precision_requirements={
'WS002': 0.015,
'WS003': 0.03
},
size_requirements={
'WS002': {'x': 450, 'y': 350, 'z': 250},
'WS003': {'x': 320, 'y': 220}
}
)
]
# 添加工单
for order in work_orders:
scheduler.add_work_order(order)
print("=== 生产排程测试 ===")
# 测试排程策略
strategies = [
SchedulingStrategy.DUE_DATE,
# SchedulingStrategy.PRIORITY,
# SchedulingStrategy.SHORTEST_FIRST,
# SchedulingStrategy.LONGEST_FIRST,
# SchedulingStrategy.EDD,
# SchedulingStrategy.SPT
]
# 在配置工作站时
for strategy in strategies:
print(f"\n使用策略: {strategy.value}")
try:
schedule_results = scheduler.schedule_by_strategy(strategy)
stats = scheduler.get_schedule_statistics()
print("\n排程统计信息:")
print(f"总工单数: {stats.get('total_orders', 0)}")
print(f"总加工时间: {stats.get('total_processing_time', 0):.2f}小时")
print(f"准时完成工单数: {stats.get('on_time_orders', 0)}")
print(f"准时率: {stats.get('on_time_rate', 0):.2f}%")
if stats.get('total_orders', 0) > 0:
print("\n排程时间窗口:")
print(f"开始时间: {stats['schedule_start'].strftime('%Y-%m-%d %H:%M:%S')}")
print(f"结束时间: {stats['schedule_end'].strftime('%Y-%m-%d %H:%M:%S')}")
print("\n设备利用率:")
for eq_id, utilization in stats.get('equipment_utilization', {}).items():
equipment = next((eq for eq in scheduler.equipment_list
if eq.equipment_id == eq_id), None)
if equipment:
print(f"设备 {equipment.name}:")
print(f" 利用率: {utilization:.2f}%")
print(f" 总工作时间: {equipment.total_working_hours:.2f}小时")
if hasattr(equipment, 'work_records') and equipment.work_records:
print(" 工作记录:")
for record in equipment.work_records:
start = record['start_time'].strftime('%Y-%m-%d %H:%M:%S')
end = record['end_time'].strftime('%Y-%m-%d %H:%M:%S')
print(f" - {start} -> {end} "
f"({record['duration']:.2f}小时)")
print("\n操作员工作负荷:")
for op_id, workload in stats.get('operator_workload', {}).items():
operator = next((op for op in scheduler.operators
if op.operator_id == op_id), None)
if operator:
print(f"操作员 {operator.name}: {workload:.2f}小时")
except Exception as e:
logger.error(f"使用策略 {strategy.value} 排程时出错: {str(e)}")
continue
except Exception as e:
logger.error(f"程序执行出错: {str(e)}")
raise
if __name__ == "__main__":
#logging.getLogger().setLevel(logging.DEBUG)
main()