基于Salabim的离散事件仿真(DES)动画交互:避免运行时修改的回溯影响
我明白你遇到的问题了——Salabim默认的Resource组件在修改可用量时会触发回溯式调度,也就是会重新检查所有历史等待的请求,这就导致你在10秒添加库存时,之前的Customer请求会被回溯处理,仿佛库存早就存在一样,这确实不符合实时调整的预期。
要解决这个问题,核心是让资源的修改只对当前仿真时间及之后的请求生效,不触动历史状态。最直接的方案是改用Salabim的Store组件替代Resource,因为Store是基于实体的库存管理,所有操作都是事件驱动的,不会回溯修改历史。
原因分析
原来的Resource是基于容量配额的,当你调用release(1)时,Salabim会自动回溯检查所有过去等待该资源的请求,看是否能在更早的时间点满足,这是它默认的资源调度逻辑,适合需要精确回溯调度的场景,但不适合你的实时交互调整需求。
而Store则是将库存作为实际的物品实体,添加物品的操作只会在当前仿真时间生效,之前的等待请求在当时没有物品,所以不会被回溯激活,只有当前及之后的请求会响应新添加的库存。
修改后的代码
import salabim as sim window_length = 20 env = sim.Environment(trace=True, time_unit='seconds') # 用Store替代Resource,模拟实体库存 stock = sim.Store("stock", capacity=5) output = sim.Store("output", capacity=5) class Customer(sim.Component): def process(self): while True: # 用get()获取库存物品,事件驱动,无回溯 self.get(stock) self.hold(sim.Uniform(2, 5)) # 将物品放入output Store self.put(output) # 监控库存和输出的数量变化 sim.AnimateMonitor( stock.level, # Store用level属性获取当前物品数量 x=0, y=-50, width=140, height=100, horizontal_scale=1/window_length*140, vertical_scale=1/stock.capacity()*90, screen_coordinates=False ) sim.AnimateMonitor( output.level, x=150, y=-50, width=140, height=100, horizontal_scale=1/window_length*140, vertical_scale=1/output.capacity()*90, screen_coordinates=False ) Customer() # 按钮动作修改为向Store放入/取出物品 sim.AnimateButton(x=-50, y=15, xy_anchor='se', width=100, text='Drop Stock', action=lambda: [stock.get() for _ in range(stock.level)] if stock.level > 0 else None) sim.AnimateButton(x=-50, y=45, xy_anchor='se', width=100, text='Add Stock', action=lambda: stock.put(1) if stock.level < stock.capacity() else None) env.animate(True) env.animation_parameters(show_fps=True, show_time=True) env.run(till=500)
关键修改点说明
- 替换组件类型:用
sim.Store替代sim.Resource,Store的level属性对应库存数量,put()/get()方法用于添加/取出物品,所有操作都是当前时间点的事件,无回溯。 - 调整请求逻辑:Customer的
process中用self.get(stock)替代self.request(stock),这是Store的标准获取方式,只会在有可用物品时触发,不会回溯历史请求。 - 按钮动作适配:添加库存时调用
stock.put(1),清空库存时循环调用stock.get()直到为空,确保操作只在当前时间生效。
测试效果
运行仿真后,前10秒库存为空,Customer会处于等待状态。当你在10秒时点击"Add Stock",库存会在10秒时增加1,Customer会立即获取该物品,等待2-5秒后放入output,不会出现回溯到10秒之前取货的情况,完全符合实时调整的预期。
如果你坚持要用Resource组件,也可以通过自定义请求逻辑,记录每个请求的发起时间,只允许当前时间之后的资源变化影响该请求,但这种方法复杂度较高,不如改用Store简洁直观。
内容的提问来源于stack exchange,提问作者AReubens

