SimPy

目录

    • 简介
        • 安装命令
        • Environment
        • Events
        • Sleep until woken up
        • Waiting for another process to terminate
        • Interrupting another process

简介

  SimPy是一个基于Python的异步事件调度器,产生一系列事件并按照仿真时间进行计划安排升序排列,在事件的循环序列中触发并执行,产生回调返回响应值,在物流,工厂制造业和餐饮服务业等工业仿真中都有所应用,比如优化快递分拣中的排班表,优化工厂制造的排班和成本,估算服务排队的时长等,还可以用于网络通信仿真.详细介绍可查阅官方文档和原理介绍,另有知乎简介.

安装命令
pip install simpy
Environment

Environment管理仿真时间的进行,让各种组件和事件按照一定的计划表进行驱动,管理仿真元素之间的关联, 主要 API 有

simpy.Environment.process  #添加仿真进程
simpy.Environment.event  #创建事件
simpy.Environment.timeout  #提供延时(timeout)事件
simpy.Environment.until  #仿真结束的条件(时间或事件)
simpy.Environment.run  #仿真启动
simpy.Environment.all_of
simpy.Environment.any_of

创建一个空的仿真环境并实例化,然后执行一段时间,这时由于没有创建任何组件和事件,所以实际上什么也没有发生,也可以使用处理进程或事件作为结束条件

env=simpy.Environment()#创建环境并实例化
env.run(until=100)#运行仿真环境,100s后停止
Events

simpy.events.Event是仿真事件,simpy是以事件作为基础组件进行构建的,各种其它类型如events.Process,events.Timeout都是以事件派生出来的,本质上还是Event.

events.Event
|
+— events.Timeout
|
+— events.Initialize
|
+— events.Process
|
+— events.Condition
|  |
|  +— events.AllOf
|  |
|  +— events.AnyOf

创建一个事件并定义响应函数,这个事件一旦调用之后就结束了,不会再响应了,在process event中也是如此.

import simpy

def my_callback(event):
    print('Called back from', event)
env = simpy.Environment()
event = env.event()
event.callbacks.append(my_callback)#给事件添加回调
event.callbacks

事件若被触发,按给定的调度事件放入队列,Event.triggered变为True,事件响应函数理完后Event.processed设为True,能获取返回值

Event.value或value=yield event

也可以将事件记为成功或失败

Event.succed(value=None)#记为成功
Event.fail(exception)#记为失败
Event.trigger(event)#记为触发

process进程也是事件

env.process()#处理时间的进程也是一个事件

所以process能产生执行另一个process,其执行结束后这个process恢复,另一个process能传给这个process返回值,创建process后,触发执行时会有Initialize事件,但不必手动处理这个.

simpy.util.start_delayed()#使process进程延迟触发
>>> from simpy.util import start_delayed
>>>
>>> def sub(env):
...     yield env.timeout(1)
...     return 23
...
>>> def parent(env):
...     start = env.now
...     sub_proc = yield start_delayed(env, sub(env), delay=3)
...     assert env.now - start == 3
...
...     ret = yield sub_proc
...     return ret
...
>>> env.run(env.process(parent(env)))
23

Condition条件事件,有时需要等待其它的事件执行出想要的结果,然后以此为条件进行事件的触发或执行,有events.AllOf和events.AnyOf事件.

>>> from simpy.events import AnyOf, AllOf, Event
>>> events = [Event(env) for i in range(3)]
>>> a = AnyOf(env, events)  # Triggers if at least one of "events" is triggered.
>>> b = AllOf(env, events)  # Triggers if all each of "events" is triggered.

也可以不用AllOf和AnyOf,转而使用逻辑运算符&,|定义条件事件

>>> def test_condition(env):
...     t1, t2 = env.timeout(1, value='spam'), env.timeout(2, value='eggs')
...     ret = yield t1 | t2
...     assert ret == {t1: 'spam'}
...
...     t1, t2 = env.timeout(1, value='spam'), env.timeout(2, value='eggs')
...     ret = yield t1 & t2
...     assert ret == {t1: 'spam', t2: 'eggs'}
...
...     # You can also concatenate & and |
...     e1, e2, e3 = [env.timeout(i) for i in range(3)]
...     yield (e1 | e2) & e3
...     assert all(e.processed for e in [e1, e2, e3])
...
>>> proc = env.process(test_condition(env))
>>> env.run()
>>> def fetch_values_of_multiple_events(env):
...     t1, t2 = env.timeout(1, value='spam'), env.timeout(2, value='eggs')
...     r1, r2 = (yield t1 & t2).values()
...     assert r1 == 'spam' and r2 == 'eggs'
...
>>> proc = env.process(fetch_values_of_multiple_events(env))
>>> env.run()
Sleep until woken up

模型为,一辆汽车在行驶一段时间后停车一段时间,停车期间进行充电,所以需要在执行停车事件时激活充电时间.

>>> from random import seed, randint
>>> seed(23)
>>>
>>> import simpy
>>>
>>> class EV:
...     def __init__(self, env):
...         self.env = env
...         self.drive_proc = env.process(self.drive(env))
...         self.bat_ctrl_proc = env.process(self.bat_ctrl(env))
...         self.bat_ctrl_reactivate = env.event()
...
...     def drive(self, env):
...         while True:
...             # Drive for 20-40 min
...             yield env.timeout(randint(20, 40))
...
...             # Park for 1–6 hours
...             print('Start parking at', env.now)
...             self.bat_ctrl_reactivate.succeed()  # "reactivate"
...             self.bat_ctrl_reactivate = env.event()
...             yield env.timeout(randint(60, 360))
...             print('Stop parking at', env.now)
...
...     def bat_ctrl(self, env):
...         while True:
...             print('Bat. ctrl. passivating at', env.now)
...             yield self.bat_ctrl_reactivate  # "passivate"
...             print('Bat. ctrl. reactivated at', env.now)
...
...             # Intelligent charging behavior here …
...             yield env.timeout(randint(30, 90))
...
>>> env = simpy.Environment()
>>> ev = EV(env)
>>> env.run(until=150)
Bat. ctrl. passivating at 0
Start parking at 29
Bat. ctrl. reactivated at 29
Bat. ctrl. passivating at 60
Stop parking at 131
Waiting for another process to terminate

上一个充电休眠的例子中会有一个问题,停车事件可能比充电时间短,一方面在停车充电,一方面停车结束正在驾驶,这时矛盾的.为此可以这样解决,开始停车时开始充电,然后只有到停车和充电都结束的时候,才开始驾驶,这里用到了条件事件&.

>>> class EV:
...     def __init__(self, env):
...         self.env = env
...         self.drive_proc = env.process(self.drive(env))
...
...     def drive(self, env):
...         while True:
...             # Drive for 20-40 min
...             yield env.timeout(randint(20, 40))
...
...             # Park for 1–6 hours
...             print('Start parking at', env.now)
...             charging = env.process(self.bat_ctrl(env))
...             parking = env.timeout(randint(60, 360))
...             yield charging & parking
...             print('Stop parking at', env.now)
...
...     def bat_ctrl(self, env):
...         print('Bat. ctrl. started at', env.now)
...         # Intelligent charging behavior here …
...         yield env.timeout(randint(30, 90))
...         print('Bat. ctrl. done at', env.now)
...
>>> env = simpy.Environment()
>>> ev = EV(env)
>>> env.run(until=310)
Start parking at 29
Bat. ctrl. started at 29
Bat. ctrl. done at 83
Stop parking at 305
Interrupting another process

现在又有一个问题,汽车只能等待电池完全充满的时候才能开始驾驶,如果将条件事件改为停车或充电结束的时候就开始驾驶,那么有时就要中断充电进程,使用process.interrupt()中断一个进程.

class EV:
    def __init__(self, env):
        self.env = env
        self.drive_proc = env.process(self.drive(env))

    def drive(self, env):
        while True:
            # Drive for 20-40 min
            yield env.timeout(randint(20, 40))

            # Park for 1 hour
            print('Start parking at', env.now)
            charging = env.process(self.bat_ctrl(env))
            parking = env.timeout(60)
            yield charging | parking
            if not charging.triggered:
                # Interrupt charging if not already done.
                charging.interrupt('Need to go!')
            print('Stop parking at', env.now)

    def bat_ctrl(self, env):
        print('Bat. ctrl. started at', env.now)
        try:
            yield env.timeout(randint(60, 90))
            print('Bat. ctrl. done at', env.now)
        except simpy.Interrupt as i:
            # Onoes! Got interrupted before the charging was done.
            print('Bat. ctrl. interrupted at', env.now, 'msg:',
                  i.cause)

>>> env = simpy.Environment()
>>> ev = EV(env)
>>> env.run(until=100)
Start parking at 31
Bat. ctrl. started at 31
Stop parking at 91
Bat. ctrl. interrupted at 91 msg: Need to go!

你可能感兴趣的:(复杂网络)