Как разделить зависимые события в SimPy

Я хочу реализовать моделирование планирования обслуживания с дискретными событиями, в котором некоторые действия по обслуживанию должны выполняться всякий раз, когда происходит другое.

Например, если стены перекрашиваются каждые 5 лет, а сухая облицовка заменяется каждые 14 лет, то стены необходимо перекрашивать при каждой замене сухой облицовки и перезапуске часов.

yr 5:  paint walls
yr 10: paint walls
yr 14: replace dry-lining
yr 14: paint walls
yr 19: paint walls
...

Я не уверен, должен ли я реализовывать каждое действие как процесс, который ссылается на зависимый процесс, или если «обслуживание стены» должно быть процессом с внутренней логикой, или каким-то другим способом.

Код, который у меня есть, имеет каждое действие как процесс с зависимым процессом, хранящимся как атрибут, но я чувствую, что, вероятно, упускаю правильный способ сделать это, поскольку я вижу, что события происходят дважды в один и тот же год.


person Jamie Bull    schedule 06.04.2016    source источник


Ответы (2)


Вы всегда должны начинать с очень простой (и неправильной) реализации, чтобы лучше понять свой вариант использования и почувствовать, как все работает, например:

import simpy


def paint_walls(env, interval):
    while True:
        yield env.timeout(interval)
        print('yr %2d: paint walls' % env.now)


def replace_dry_lining(env, interval):
    while True:
        yield env.timeout(interval)
        print('yr %d: replace dry-lining' % env.now)


env = simpy.Environment()
env.process(paint_walls(env, interval=5))
env.process(replace_dry_lining(env, interval=14))
env.run(until=20)

Выход:

yr  5: paint walls
yr 10: paint walls
yr 14: replace dry-lining
yr 15: paint walls

Затем вы можете начать расширять/модифицировать вашу симуляцию. Вот два варианта того, как можно смоделировать вашу проблему:

Вариант А

Мы по-прежнему используем два отдельных процесса, но нам нужен способ обмена событием «сухая облицовка заменена» между ними, чтобы мы могли также покрасить стену:

import simpy


class Maintenance:
    PAINT_WALL_INTERVAL = 5
    REPLACE_DRY_LINING_INTERVAL= 14

    def __init__(self, env):
        self.env = env
        self.dry_lining_replaced = env.event()

        self.p_paint_walls = env.process(self.paint_walls())
        self.p_replace_dry_lining = env.process(self.replace_dry_lining())

    def paint_walls(self):
        timeout = self.PAINT_WALL_INTERVAL
        while True:
            yield self.env.timeout(timeout) | self.dry_lining_replaced
            print('yr %2d: paint walls' % self.env.now)

    def replace_dry_lining(self):
        timeout = self.REPLACE_DRY_LINING_INTERVAL
        while True:
            yield self.env.timeout(timeout)
            print('yr %2d: replace dry-lining' % self.env.now)
            self.dry_lining_replaced.succeed()
            self.dry_lining_replaced = self.env.event()


env = simpy.Environment()
m = Maintenance(env)
env.run(until=20)

Выход:

yr  5: paint walls
yr 10: paint walls
yr 14: replace dry-lining
yr 14: paint walls
yr 19: paint walls

Вариант Б

Мы также можем смоделировать это с помощью всего одного процесса, который ожидает либо события «покраска стен», либо события «замена сухой облицовки»:

import simpy


def maintenance(env):
    PAINT_WALL_INTERVAL = 5
    REPLACE_DRY_LINING_INTERVAL = 14

    paint_wall = env.timeout(PAINT_WALL_INTERVAL)
    replace_dry_lining = env.timeout(REPLACE_DRY_LINING_INTERVAL)

    while True:
        results = yield paint_wall | replace_dry_lining
        do_paint = paint_wall in results
        do_replace = replace_dry_lining in results

        if do_replace:
            print('yr %2d: replace dry-lining' % env.now)
            replace_dry_lining = env.timeout(REPLACE_DRY_LINING_INTERVAL)

        if do_paint or do_replace:
            print('yr %2d: paint walls' % env.now)
            paint_wall = env.timeout(PAINT_WALL_INTERVAL)


env = simpy.Environment()
env.process(maintenance(env))
env.run(until=20)

Выход:

yr  5: paint walls
yr 10: paint walls
yr 14: replace dry-lining
yr 14: paint walls
yr 19: paint walls
person Stefan Scherfke    schedule 07.04.2016
comment
Спасибо за это. Есть ли причина, по которой ответ, к которому я пришел, неверен? Причина, по которой я остановился на этом, заключается в том, что я могу объединить несколько зависимых действий вместе, каждому из которых нужно знать только следующее в цепочке. - person Jamie Bull; 07.04.2016
comment
Истинный! Теперь я добавил более полный пример. - person Jamie Bull; 07.04.2016

Вот такой подход я выбрал в конце:

import simpy
from simpy.events import Interrupt


class Construction(object):

    def __init__(self, name, components):
        self.name = name
        self.components = components
        self.link_components()

    def link_components(self):
        """Link each component to the next outermost component      
        """
        for i, component in enumerate(self.components):
            try:
                component.dependent = self.components[i+1]
            except IndexError:
                component.dependent = None                

class Component(object):

    def __init__(self, env, name, lifespan):
        """Represents a component used in a construction.

        """
        self.env = env
        self.name = name
        self.lifespan = lifespan    
        self.action = env.process(self.run())

    def run(self):
        while True:
            try:
                yield self.env.timeout(self.lifespan)
                self.replace()
            except Interrupt:  # don't replace
                pass

    def replace(self):
        print "yr %d:  replace %s" % (env.now, self.name)
        if self.dependent:
            self.dependent.action.interrupt()  # stop the dependent process
            self.dependent.replace()  # replace the dependent component


env = simpy.Environment()
components = [Component(env, 'structure', 60),
              Component(env, 'dry-lining', 14),
              Component(env, 'paint', 5)]
wall = Construction('wall', components)
env.run(until=65)
person Jamie Bull    schedule 06.04.2016