APS开源源码解读: 排程工具 frepple

https://github.com/frePPLe/frepple/tree/master

class OperatorDelete:
    def __init__(self):
        # Initialize the class

    @classmethod
    def initialize(cls):
        # Initialize the metadata and Python class

    @classmethod
    def create(cls, args, kwds):
        try:
            # Create the solver
            s = OperatorDelete()

            # Iterate over extra keywords and set attributes

            return s
        except Exception as e:
            # Handle exceptions
            pass

    @classmethod
    def solve(cls, v):
        # Implement the solve method
        try:
            # Free Python interpreter for other threads
            Py_BEGIN_ALLOW_THREADS

            # Perform the solving logic
            if v is None:
                # Delete all excess
                self.solve()
            elif isinstance(v, Demand):
                # Delete upstream of a single demand
                self.solve_demand(v)
            elif isinstance(v, Buffer):
                # Delete upstream of a single buffer
                self.solve_buffer(v)
            elif isinstance(v, Resource):
                # Delete upstream of a single resource
                self.solve_resource(v)
            elif isinstance(v, OperationPlan):
                # Delete an operation plan
                self.solve_operation_plan(v)

        except Exception as e:
            # Handle exceptions
            pass
        finally:
            # Reclaim Python interpreter
            Py_END_ALLOW_THREADS

    @classmethod
    def solve_operation_plan(cls, o, v):
        # Implement solve for operation plan
        if not o:
            return  # Null argument passed

        # Mark all buffers.
        # The batching solver doesn't like that we push both consumers and producers,
        # but ideally we would pass true for both arguments.
        self.push_buffers(o, True, False)

        # Delete the operation plan
        if o.get_proposed():
            if self.cmds:
                self.cmds.add(CommandDeleteOperationPlan(o))
            else:
                del o

        # Propagate to all upstream buffers
        while buffersToScan:
            curbuf = buffersToScan.pop()
            self.solve_buffer(curbuf)

    @classmethod
    def solve_resource(cls, r, v):
        # Implement solve for a resource
        if self.get_log_level() > 0:
            print(f"Scanning {r} for excess")

        # Loop over all operation plans on the resource
        for op_plan in r.get_load_plans():
            if op_plan.get_event_type() == 1:
                # Add all buffers into which material is produced to the stack
                self.push_buffers(op_plan, False, True)

        # Process all buffers found, and their upstream colleagues
        while buffersToScan:
            cur_buf = buffersToScan.pop()
            self.solve_buffer(cur_buf)

    @classmethod
    def solve_demand(cls, d, v):
        # Implement solve for a demand
        if self.get_log_level() > 1:
            print(f"Scanning {d} for excess")

        # Delete all delivery operation plans.
        while True:
            # Find a candidate operation plan to delete
            candidate = None
            delivery_plans = d.get_delivery()

            for op_plan in delivery_plans:
                if op_plan.get_proposed():
                    candidate = op_plan
                    break

            if not candidate:
                break

            # Push the buffer on the stack in which the deletion creates excess inventory
            self.push_buffers(candidate, True, False)

            # Delete only the delivery, immediately or through a delete command
            if self.cmds:
                self.cmds.add(CommandDeleteOperationPlan(candidate))
            else:
                del candidate

    @classmethod
    def push_buffers(cls, o, consuming, producing):
        # Implement pushing buffers
        # Loop over all flow plans
        for flow_plan in o.get_flow_plans():
            # Skip flow plans we're not interested in
            if not (
                (consuming and flow_plan.get_quantity() < 0)
                or (producing and flow_plan.get_quantity() > 0)
            ):
                continue

            # Check if the buffer is already found on the stack
            found = False
            for buf in buffersToScan:
                if buf == flow_plan.get_buffer():
                    found = True
                    break

            # Add the buffer to the stack if not found
            if not found:
                buffersToScan.append(flow_plan.get_buffer())

        # Recursive call for all sub-operation plans
        for sub_op_plan in o:
            self.push_buffers(sub_op_plan, consuming, producing)

    @classmethod
    def solve_buffer(cls, b, v):
        # Implement solve for a buffer
        if self.get_log_level() > 1:
            print(f"Scanning buffer {b}")

        # Get the list of flow plans for the buffer
        flow_plans = b.get_flow_plans()
        
        fiter = iter(flow_plans)
        fend = None  # Replace with the actual end condition for the flow plans

        if fiter is fend:
            return  # No flow plans in the buffer

        excess = fiter.get_onhand() - fiter.get_min()

        if excess > ROUNDING_ERROR:
            fiter = iter(flow_plans)
            while excess > ROUNDING_ERROR and fiter is not fend:
                if fiter.get_quantity() <= 0:
                    # Not a producer
                    fiter = iter(fiter)
                    continue

                fp = None
                if fiter.get_event_type() == 1:
                    fp = fiter

                if not fp or not fp.get_operation_plan().get_proposed() or \
                        fp.get_operation_plan().get_demand() or \
                        (fp.get_operation_plan().get_owner() and \
                        fp.get_operation_plan().get_owner().get_demand()) or \
                        fp.get_flow().has_type(FlowTransferBatch):
                    # It's locked or a delivery operation plan
                    fiter = iter(fiter)
                    continue

                cur_excess = b.get_excess(iter(fiter))
                if fp:
                    for flow_plan in fp.get_operation_plan().get_flow_plans():
                        if flow_plan.get_quantity() < ROUNDING_ERROR or \
                                flow_plan.get_buffer() == b or \
                                not flow_plan.get_flow().get_quantity():
                            continue
                        my_excess = (b.get_excess(flow_plan) - \
                                flow_plan.get_flow().get_quantity_fixed()) * \
                                fp.get_flow().get_quantity() / \
                                flow_plan.get_flow().get_quantity()
                        if my_excess >= 0.0 and my_excess < cur_excess:
                            cur_excess = my_excess

                if cur_excess < ROUNDING_ERROR:
                    fiter = iter(fiter)
                    continue

                while fiter is not fend and fiter.get_event_type() == 1 and \
                        fiter.get_operation_plan().get_top_owner() == \
                        fp.get_operation_plan().get_top_owner():
                    fiter = iter(fiter)

                new_size_opplan = None  # Calculate the new size as needed
                new_size_flowplan = None  # Calculate the new size as needed

                if cur_excess < fp.get_flow().get_quantity_fixed() + \
                        fp.get_operation().get_size_multiple() * \
                        fp.get_flow().get_quantity():
                    # This excess is unavoidable
                    fiter = iter(fiter)
                    continue
                elif cur_excess >= fiter.get_quantity() - ROUNDING_ERROR:
                    # Completely delete the producer
                    new_size_opplan = 0.0
                    new_size_flowplan = 0.0
                else:
                    # Resize the producer
                    # We need to keep the operation plan start date constant
                    # during the resize
                    # Calculate new sizes and apply them

                if new_size_flowplan < ROUNDING_ERROR:
                    # The complete operation plan is excess
                    excess -= fiter.get_quantity()
                    self.push_buffers(fp.get_operation_plan(), True, False)

                    if self.cmds:
                        self.cmds.add(CommandDeleteOperationPlan(fp.get_operation_plan()))
                    else:
                        del fp.get_operation_plan()
                else:
                    # Reduce the operation plan
                    # Add upstream buffers to the stack
                    self.push_buffers(fp.get_operation_plan(), True, False)
                    excess -= fiter.get_quantity() - new_size_flowplan

                    # Resize operation plan if needed
                    if self.cmds:
                        # TODO: Adjust the command or operation plan resizing logic
                        self.cmds.add(CommandMoveOperationPlan(
                            fp.get_operation_plan(), Date.infinite_past,
                            fp.get_operation_plan().get_end(), new_size_opplan))
                    else:
                        # Set the new size for the operation plan
                        fp.get_operation_plan().set_quantity(new_size_opplan)

                fiter = iter(fiter)

    @staticmethod
    def solve_python(self, args):
        # Implement the solve method with Python-specific code

  • 关键在于理解buffer的含义以及操作。
  • 有consumer和producing, 数量有正负

Identify Critical Paths: Start by identifying the critical paths in your scheduling process. Critical paths are sequences of tasks that have the least flexibility in terms of start times. These paths are essential to meeting project deadlines or other constraints.

Determine Buffer Locations: Based on your critical paths, identify locations in your schedule where buffers can be introduced. These locations are typically at the end of a critical path or just before a critical constraint.

Size the Buffer: The buffer’s size depends on factors such as variability in task durations, uncertainty in resource availability, and the desired level of risk mitigation. It’s often calculated based on statistical analysis, like the Critical Chain Project Management (CCPM) method or Monte Carlo simulations.

Set Buffer Policies: Define policies that determine when and how the buffer is consumed. Common buffer policies include:

Start Buffer: This is added at the start of the project. It ensures that the project starts on time.
Resource Buffer: This buffer is used to manage resource constraints. It ensures that critical resources are available when needed.
Feeding Buffer: Placed before critical constraints or dependent tasks, this buffer ensures that inputs are available as needed.
Monitoring and Control: Continuously monitor the progress of your project. If tasks start to encroach on the buffer, take action to bring the project back on track. This might involve reallocating resources, addressing bottlenecks, or resequencing tasks.

Buffer Management: The focus should be on protecting the project buffer. If it starts to be consumed, assess why this is happening and take corrective action.

Project Review: After the project is completed, conduct a review to understand why the buffer was consumed. This can help in making improvements for future projects.

Flexibility: Keep in mind that the buffer provides flexibility, but it’s important to strike a balance. Having too much buffer may result in inefficient resource allocation, while too little buffer might not provide enough protection.

In the context of backward scheduling, the buffer helps to ensure that scheduled tasks can start as soon as possible while accounting for constraints and uncertainties. It’s essentially a cushion that provides protection against delays without compromising the overall project timeline. The goal is to optimize the use of resources and time, minimize disruptions, and ensure that the project is completed on time.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YueTann

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值