【SimPy系列博客之官方example学习与解读】—— Example 2: Machine Shop

Hello,CSDN的各位小伙伴们,又见面啦!今天我们要学习的例程是:Machine Shop!我们开始吧!

例程背景

有一个工厂,工厂里面有 n 台一样的机器,每台机器都需要不停地加工一个又一个的零件。但是机器会周期性地发生故障,一旦机器发生故障,工厂里面唯一的一个维修工 repairman 就需要立即放下手里原本那个没那么紧急的任务,转而去维修机器,当修好机器后,维修工又会继续做他原本被打断的那个任务。我们的目的就是用 SimPy 对这个流程进行建模。

例程代码分析

类似地,一些头文件和参数的定义就不再赘述,直接贴代码

import random
import simpy

RANDOM_SEED = 2024
PT_MEAN = 10.0  # 一个零件制作的平均时间
PT_SIGMA = 2.0  # 一个零件制作时间的方差
MTTF = 300.0  # machine损坏的平均时间
BREAK_MEAN = 1 / MTTF  # machine的损坏时间服从指数分布
REPAIR_TIME = 30.0  # 维修时间
JOB_DURATION = 30.0  # repairman另一个没那么紧急的工作的持续时间
NUM_MACHINES = 10  # 机器的数量
WEEKS = 4
SIM_TIME = WEEKS * 7 * 24 * 60  # 总仿真时间

下面才是重点部分,我们首先来定义一个零件的加工需要多长时间,这个比较简单,如下:

# 定义machine制作一个零件的时间
def time_per_part():
    t = random.normalvariate(PT_MEAN, PT_SIGMA)
    while t <= 0:
        t = random.normalvariate(PT_MEAN, PT_SIGMA)
    return t

接下来,我们需要定义machine什么时候会出故障:

# 定义machine什么时候损坏
def time_to_failure():
    return random.expovariate(BREAK_MEAN)

然后,我们将machine作为一个类,machine这个类需要实现什么功能呢?首先第一个就是正常的生产零件(在没有故障的时候),第二个则是发生故障后请求repairman资源对自己进行修复。

# 下面定义machine的行为
class Machine:
    def __init__(self, env, name, repairman):
        self.env = env
        self.name = name  # machine的编号
        self.parts_made = 0  # 统计该机器一共制造了多少个零件
        self.broken = False  # 指示该机器是否发生故障

        self.process = env.process(self.working(repairman))
        env.process(self.break_machine())

    def working(self, repairman):
        while True:  # 表示机器一直会去生产零件
            to_produce = time_per_part()
            while to_produce:
                start = self.env.now
                try:
                    # 如果机器没有出故障就正常花时间加工零件
                    yield self.env.timeout(to_produce)
                    to_produce = 0
                except simpy.Interrupt:
                    # 被打算则说明机器出故障了,需要repairman来立即进行修复
                    self.broken = True

                    # 先计算上一个零件生产了多久
                    already_produce = self.env.now - start

                    # 下面计算上一个零件还要多久才做完
                    to_produce -= already_produce

                    with repairman.request(priority=1) as req:
                        # "machine故障需要维修"这个process很紧急,因此设为高优先级
                        yield req  # 等待repairman资源
                        print('Repairman come to repair machine: ', self.name, ' at: ', self.env.now)
                        yield self.env.timeout(REPAIR_TIME)
                        print('Repair Done at: ', self.env.now)

            self.parts_made += 1  # 表示一个零件制作完成

    def break_machine(self):
        while True:
            yield self.env.timeout(time_to_failure())
            if not self.broken:  # 只有在machine正常工作时才会打断
                print('Machine: ', self.name, ' broken at time: ', self.env.now)
                self.process.interrupt()

上面的代码中,当我们定义了working函数,并利用env.process(working(…))将其加入到仿真环境后,他就变成了一个process,可以调用process的interrupt对其进行打断。值得注意的是:如果working被打断了,它会保存打断前自己的执行状态,当打断working的process执行完毕后(也就是except内的程序执行完毕后),working还是接着打断前的状态继续执行

我们接着看 working函数里面 except simpy.Interrupt:里面的部分:在正常加工零件被打断后,我们需要记录这个零件还要加工多久,方便我们后面machine被修复后,继续加工。因为machine坏了之后自然需要请求repairman进行修复,所以有:with repairman.request(priority=1) as req:,其中,priority表示这个请求的优先级,数字越小表示优先级越高。yield req 则表示machine会一直等,直到repairman对其进行修复。(不像我们在example 1中的情况,用户还有一个patience值)

我们知道了machine的活动,接下来还需要定义repairman的另一个没那么紧急的任务(也是一个process,也需要请求repairman资源,只不过优先级不如对machine的修复)

def other_jobs(env, repairman):
    """
    定义repairman的另外一个没那么紧急的工作
    :param env: 仿真环境
    :param repairman: 表示一个preemptive资源
    :return:
    """

    while True:
        to_do = JOB_DURATION
        while to_do:
            with repairman.request(priority=2) as req:
                yield req
                start = env.now
                try:
                    # 如果没有被"维修“这一更紧急的任务打断,则正常做这个任务
                    yield env.timeout(to_do)
                    to_do = 0
                except simpy.Interrupt:
                    # 先计算这个任务已经做了多久
                    already_do = env.now - start

                    # 计算这个任务还要多久
                    to_do -= already_do

这里可能大家会有疑问:我们刚刚说某个process,通过调用另一个process的interrupt方法,实现对另一个process的打断。可是在other_jobs这个函数中,似乎没有其他敌方调用对other_jobs的打断,为什么还会出现 except simpy.Interrupt:呢?原因在于,本例中使用了两种方法对process进行打断,第一种是显式的调用 interrupt方法,另一种是通过 preemptiveresource,优先级高的process会抢占这个资源,从而实现对低优先级process的打断。所以,只要repair machine的那个process以更高的优先级获得了repairman的资源,那么这个other_jobs的process就会自动被打断。

那么最后,我们创建仿真环境、实例化Machine类、定义资源等,就能够开始执行仿真:

print('Example 2: Machine Shop...')
random.seed(RANDOM_SEED)

env = simpy.Environment()
repairman = simpy.PreemptiveResource(env, capacity=1)
machines = [Machine(env, f'Machine {
     i}', repairman) for i in range(NUM_MACHINES)]  # 实例化machine类
env.process(other_jobs(env, repairman))

env.run(until=SIM_TIME)

for machine in machines:
    print('Machine: ', machine.name, ' has made: ', machine.parts_made)

输出结果如下:

Example 2: Machine Shop...
Machine:  Machine 8  broken at time:  18.07951144463326
Repairman come to repair machine:  Machine 8  at:  18.07951144463326
Machine:  Machine 9  broken at time:  39.77961184319781
Repair Done at:  48.079511444633255
Repairman come to repair machine:  Machine 9  at:  48.079511444633255
Machine:  Machine 7  broken at time:  48.18142965423535
Repair Done at:  78.07951144463325
Repairman come to repair machine:  Machine 7  at:  78.07951144463325
Repair Done at:  108.07951144463325
Machine:  Machine 0  broken at time:  108.61453162604185
Repairman come to repair machine:  Machine 0  at:  108.61453162604185
Repair Done at:  138.61453162604187
Machine:  Machine 6  broken at time:  189.63437415331165
Repairman come to repair machine:  Machine 6  at:  189.63437415331165
Repair Done at:  219.63437415331165
Machine:  Machine 4  broken at time:  365.2793434875798
Repairman come to repair machine:  Machine 4  at:  365.2793434875798
Machine:  Machine 1  broken at time:  378.28402258242255
Machine:  Machine 3  broken at time:  390.3204213097364
Repair Done at:  395.2793434875798
Repairman come to repair machine:  Machine 1  at:  395.2793434875798
Repair Done at:  425.2793434875798
Repairman come to repair machine:  Machine 3  at:  425.2793434875798
Repair Done at:  455.2793434875798
Machine:  Machine 2  broken at time:  502.3235610496023
Repairman come to repair machine:  Machine 2  at:  502.3235610496023
Repair Done at:  532.3235610496023
Machine:  Machine 5  broken at time:  2398.7360300209943
Repairman come to repair machine:  Machine 5  at:  2398.7360300209943
Repair Done at:  2428.7360300209943
Machine:  Machine 0  has made:  4026
Machine:  Machine 1  has made:  4019
Machine:  Machine 2  has made:  4029
Machine:  Machine 3  has made:  4037
Machine:  Machine 4  has made:  4042
Machine:  Machine 5  has made:  4035
Machine:  Machine 6  has made:  4020
Machine:  Machine 7  has made:  4012
Machine:  Machine 8  has made:  4028
Machine:  Machine 9  has made:  4046

其中,我们注意到:machine 9发生故障后,由于machine 8还没修好,所以它就得等到machine 8修好,才能获得repairman的资源。

相关推荐

  1. Jar Summery 2 Linux Service Configure Example

    2024-01-09 19:14:02       11 阅读
  2. apisix 官方example,单机docker的etcd备份和恢复

    2024-01-09 19:14:02       35 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-01-09 19:14:02       14 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-09 19:14:02       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-09 19:14:02       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-09 19:14:02       18 阅读

热门阅读

  1. React-路由进阶

    2024-01-09 19:14:02       33 阅读
  2. 智能寻迹避障清障机器人设计(摘 要)

    2024-01-09 19:14:02       40 阅读
  3. 布隆过滤器的原理

    2024-01-09 19:14:02       24 阅读
  4. 编程笔记 html5&css&js 025 HTML输入类型(1/2)

    2024-01-09 19:14:02       35 阅读
  5. Qt隐式共享浅析

    2024-01-09 19:14:02       33 阅读
  6. 【前端】JQuery(学习笔记)

    2024-01-09 19:14:02       31 阅读