Simpy:如何表示火车地铁模拟中的故障?

Simpy: How can I represent failures in a train subway simulation?

这里是 python 新用户,也是这个伟大网站上的第一个 post 用户。我一直无法找到我的问题的答案,所以希望它是独一无二的。

我正在尝试使用 simpy 创建一个火车 subway/metro 模拟,系统中会定期出现故障和修复。这些故障发生在火车上,也发生在轨道部分和站台上的信号上。我已经阅读并应用了官方的 Machine Shop 示例(您可以在附加代码中看到相似之处),因此设法通过中断其 'journey time'.

对随机故障和火车维修进行建模

但是我还没有想出如何模拟火车所经过的路线上的信号故障。我目前只是指定从 A 到 B 的旅行时间,这确实会被打断,但只是由于火车故障。

是否可以将每个行程定义为自己的流程,即 A_to_B 和 B_to_C 部分的单独流程,以及 pA、pB 和 pC 等独立平台。每一个都有单一的资源(一次只允许一列火车)并为这些部分和平台流程合并随机故障和维修?我可能还需要在两个平台之间有几个部分,其中任何一个都可能会失败。

如有任何帮助,我们将不胜感激。

到目前为止,这是我的代码:

import random
import simpy
import numpy

RANDOM_SEED = 1234

T_MEAN_A = 240.0 # mean journey time
T_MEAN_EXPO_A = 1/T_MEAN_A # for exponential distribution

T_MEAN_B = 240.0 # mean journey time
T_MEAN_EXPO_B = 1/T_MEAN_B # for exponential distribution

DWELL_TIME = 30.0 # amount of time train sits at platform for passengers
DWELL_TIME_EXPO = 1/DWELL_TIME

MTTF = 3600.0  # mean time to failure (seconds)
TTF_MEAN = 1/MTTF # for exponential distribution

REPAIR_TIME = 240.0
REPAIR_TIME_EXPO = 1/REPAIR_TIME

NUM_TRAINS = 1
SIM_TIME_DAYS = 100
SIM_TIME = 3600 * 18 * SIM_TIME_DAYS
SIM_TIME_HOURS = SIM_TIME/3600


# Defining the times for processes
def A_B(): # returns processing time for journey A to B
    return random.expovariate(T_MEAN_EXPO_A) + random.expovariate(DWELL_TIME_EXPO)

def B_C(): # returns processing time for journey B to C
    return random.expovariate(T_MEAN_EXPO_B) + random.expovariate(DWELL_TIME_EXPO)

def time_to_failure(): # returns time until next failure
    return random.expovariate(TTF_MEAN)


# Defining the train
class Train(object):

    def __init__(self, env, name, repair):
        self.env = env
        self.name = name
        self.trips_complete = 0
        self.broken = False

    # Start "travelling" and "break_train" processes for the train
        self.process = env.process(self.running(repair))
        env.process(self.break_train())

    def running(self, repair):
        while True:
        # start trip A_B
            done_in = A_B()
            while done_in:
                try:
                    # going on the trip
                    start = self.env.now
                    yield self.env.timeout(done_in)
                    done_in = 0 # Set to 0 to exit while loop

                except simpy.Interrupt:
                    self.broken = True
                    done_in -= self.env.now - start # How much time left?
                    with repair.request(priority = 1) as req:
                        yield req
                        yield self.env.timeout(random.expovariate(REPAIR_TIME_EXPO))
                    self.broken = False
        # Trip is finished
            self.trips_complete += 1

        # start trip B_C
            done_in = B_C()
            while done_in:
                try:
                    # going on the trip
                    start = self.env.now
                    yield self.env.timeout(done_in)
                    done_in = 0 # Set to 0 to exit while loop

                except simpy.Interrupt:
                    self.broken = True
                    done_in -= self.env.now - start # How much time left?
                    with repair.request(priority = 1) as req:
                        yield req
                        yield self.env.timeout(random.expovariate(REPAIR_TIME_EXPO))
                    self.broken = False
        # Trip is finished
            self.trips_complete += 1


    # Defining the failure      
    def break_train(self):
        while True:
            yield self.env.timeout(time_to_failure())
            if not self.broken:
            # Only break the train if it is currently working
                self.process.interrupt()


# Setup and start the simulation
print('Train trip simulator')
random.seed(RANDOM_SEED) # Helps with reproduction

# Create an environment and start setup process
env = simpy.Environment()
repair = simpy.PreemptiveResource(env, capacity = 1)
trains = [Train(env, 'Train %d' % i, repair)
    for i in range(NUM_TRAINS)]

# Execute
env.run(until = SIM_TIME)


# Analysis
trips = []
print('Train trips after %s hours of simulation' % SIM_TIME_HOURS)
for train in trains:
    print('%s completed %d trips.' % (train.name, train.trips_complete))
    trips.append(train.trips_complete)

mean_trips = numpy.mean(trips)
std_trips = numpy.std(trips)
print "mean trips: %d" % mean_trips
print "standard deviation trips: %d" % std_trips

看起来你在使用 Python 2,这有点不幸,因为 Python 3.3 及更高版本为 Python 生成器提供了更多灵活性。但 尽管如此,您的问题应该可以在 Python 2 中解决。

您可以在流程中使用子流程:

def sub(env):
    print('I am a sub process')
    yield env.timeout(1)
    # return 23  # Only works in py3.3 and above
    env.exit(23)  # Workaround for older python versions

def main(env):
    print('I am the main process')
    retval = yield env.process(sub(env))
    print('Sub returned', retval)

如您所见,您可以使用 Process 个由 Environment.process() 编写的实例 return 像正常事件一样。您甚至可以在子流程中使用 return 值。

如果您使用 Python 3.3 或更新版本,则不必显式启动新的 子进程,但可以使用 sub() 作为子例程,而只是转发 它产生的事件:

def sub(env):
    print('I am a sub routine')
    yield env.timeout(1)
    return 23

def main(env):
    print('I am the main process')
    retval = yield from sub(env)
    print('Sub returned', retval)

您还可以将信号建模为可以使用的资源 通过失败过程或火车。如果失败进程请求信号 起初,火车必须在信号灯前等待,直到故障 进程释放信号资源。如果火车已经通过 信号(因此具有资源),信号不能中断。我不认为 这是个问题,因为火车无论如何也停不下来。如果应该的话 有问题,只需使用 PreemptiveResource。

希望对您有所帮助。欢迎加入我们的 mailing list 了解更多 讨论。