ERP工单排程实例

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()


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值