Как смоделировать приоритет задач?

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

Например:

      /--->A --->B\
Start ---->C------->End
      \--->D---->E/

Это означает, что задачи A, C и D могут выполняться одновременно, но задача B должна выполняться после задачи A, а задача E — после задачи D.

Я написал этот код:

#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
import simpy


class Task(object):
    """ 
        Has an id i.e.: 'E',
        duration i.e.: 5
        a list with Task that precede the current
    """
    def __init__(self, idTask, duration, env, previousTasks=None):
        self.idTask = idTask
        self.duration = duration
        self.env = envs
        self.completed = env.event()
        self.action = env.process(self.run())
        if previousTasks is None:
            self.previousTasks = []
        else:
            self.previousTasks = previousTasks

    def run(self):
        while self.can_execute() and not self.completed.triggered:
            print "Starting task %s at time %s" % (self.idTask, self.env.now)
            yield self.env.timeout(self.duration)
            print "Completed task %s in time %s" % (self.idTask, self.env.now)
            self.completed.succeed(True)

    def can_execute(self):
        result = True
        for task in self.previousTasks:
            if not task.completed.triggered:
                result = False
                break
        return result

if __name__ == "__main__":
    env = simpy.Environment()
    taskA = Task('A', 4, env)
    taskB = Task('B', 5, env, [taskA])
    env.run(until=20)

Атрибут Completed в объекте Task предназначен для того, чтобы узнать, завершена ли текущая задача. (Я пытался сделать это с логическим значением без каких-либо результатов.)

Задача В имеет задачу А в качестве предшествующей задачи. Это означает, что задача В должна начаться до того, как завершится задача А, но вот результат, когда я ее запускаю:

% python tasks.py
Starting task A at time 0
Completed task A at time 4
%

Я не понимаю, почему не удалось запустить taskB.

---------------------------------------------------------------------------

РЕШЕНО

Я использовал метод start_delayed, доступный в модуле simpy.util от Simpy. В зависимости от того, выполняет ли текущая задача предыдущие задачи, действие каждой задачи представляет собой простой процесс или отложенный процесс.
Задержка для каждой задачи рассчитывается в add_precedences и Assessment_delays, но она необходима для суммирования продолжительности предыдущих задач. на предыдущую задачу текущей.
Вот окончательный код:

#!/usr/bin/python2.7
# -*- coding: utf-8 -*-
import simpy
from simpy.util import start_delayed

delays = {}
completed = []


class Task(object):
    """ Has an id i.e.: 'E',
        duration i.e.: 5
        a list with Task that precede the current
    """
    def __init__(self, id, duration, env, previousTasks=None):
        self.id = id
        self.duration = duration
        self.env = env
        if previousTasks is None:
            self.previousTasks = []
        else:
            self.previousTasks = previousTasks
        self.action = None

    def run(self):
        while True:
            if delays[self.id] == self.env.now:
                print "Start task: %s at time: %s" % (self.id, self.env.now)
                yield self.env.timeout(self.duration)
                completed.append(self.id)
                print "Finish task: %s at time: %s" % (self.id, self.env.now)
            else:
                if self.id in completed:
                    self.env.exit()


def add_precedences(prevTask, durations):
    if len(prevTask) == 0:
        return 0
    else:
        durations.extend(map(lambda x: x.duration, prevTask))
        for prev in prevTask:
            add_precedences(prev.previousTasks, durations)
        return sum(durations)


def estimate_delays(tasks):
    result = {}
    for task in tasks:
        durations = []
        total = (add_precedences(task.previousTasks, durations))
        result.update({task.id: total})
    return result


def set_action(tasks):
    for task in tasks:
        if delays[task.id] > 0:
            task.action = start_delayed(task.env, task.run(), delays[task.id])
        else:
            task.action = env.process(task.run())

if __name__ == '__main__':
    env = simpy.Environment()
    taskA = Task('A', 4, env)
    taskB = Task('B', 5, env, previousTasks=[taskA])
    taskC = Task('C', 2, env, previousTasks=[taskB])
    tasks = [taskA, taskB, taskC]
    delays.update(estimate_delays(tasks))
    set_action(tasks)
    env.run(until=20)

В этом случае в проекте 3 задачи («А», «Б», «С») и вот дерево:

Start --->A--->B--->C--->End 

B должен начаться во время: 4 (потому что это продолжительность A). И C должен начаться в момент времени: 9 (потому что это продолжительность B + A).
Чтобы узнать, какая задача уже завершена, я создал список, в котором каждая задача добавляет свой собственный идентификатор. Если в этом списке есть собственный идентификатор, каждая задача удаляется из среды.


person pazitos10    schedule 09.05.2015    source источник
comment
Небольшой комментарий к вашему коду: вы не должны использовать изменяемые аргументы по умолчанию, они могут привести к неожиданностям. Чаще всего делают что-то вроде def __init__(..., previousTasks=None): if previousTasks is None: previousTasks = [].   -  person Bas Swinckels    schedule 09.05.2015
comment
Вы правы, я отредактирую это прямо сейчас, спасибо!   -  person pazitos10    schedule 09.05.2015


Ответы (1)


Я должен сделать это как комментарий, но у меня нет репутации для этого. Тем не менее, я думаю, что это может помочь с вашей проблемой.

Я не знаком с simpy, но вы можете спутать триггерный и обработанный, что не позволяет выполнить задачу B; проверьте эту ссылку: http://simpy.readthedocs.org/en/latest/api_reference/simpy.events.html#module-simpy.events

person pbc1303    schedule 09.05.2015
comment
Привет! Я пробовал с self.completed.processed вместо триггера, но он показывает мне следующее: RuntimeError: ‹Event() object ...› уже был запущен. Но раньше симуляция выдает, что запущена только задача A. - person pazitos10; 10.05.2015
comment
Ok. Думаю, теперь я понял. Когда задача B входит в функцию выполнения, она не может выполняться, поскольку A является предшественником, поэтому она существует в функции и никогда не возвращается к ней. Возможно, вам следует использовать цикл while, внешний по отношению к циклу в функции запуска (что было бы не очень эффективно). Идеальным способом кажется попытка подать сигнал задаче B о том, что она может запускаться. Так понятно? - person pbc1303; 10.05.2015
comment
Это именно то, что происходит, потому что я пытался использовать класс Event для завершенного атрибута. Но я не могу сигнализировать другой задаче с помощью Simpy, и если бы я мог, я должен был бы иметь запись о максимальной продолжительности предшественников и разбудить ее, когда env.now == timeToWake. Верно? - person pazitos10; 10.05.2015
comment
Да, то, что вы сказали, кажется правильным. Но я думаю, вы должны попытаться немного изменить свою реализацию. Возможно, попробуйте использовать класс simpy.events.Condition, в документации говорится, что это событие, которое запускается, когда выполняется список условий. В вашем случае список условий — это список предшествующих задач, которые необходимо выполнить. - person pbc1303; 10.05.2015
comment
Я изменю свою реализацию, используя указанный вами класс. Большое спасибо!. отпишусь о результатах :) - person pazitos10; 10.05.2015
comment
проблема решена, спасибо за помощь!! Поздравления из Аргентины. Извините за мой английский - person pazitos10; 11.05.2015
comment
Без проблем. Рад, что смог помочь! - person pbc1303; 11.05.2015