I am trying to write a simple simulation of a queuing system:
The input process is:
import simpy
class InputProcess(object):
def __init__(self, env: simpy.Environment, name: str, freq: float):
self.env = env
self.name = name
self.period = 1/freq
self.counter = 0
self.action = env.process(self.run())
def create_cust(self):
cust_name = f"{self.name}-{self.counter}"
self.counter += 1
return cust_name
def run(self):
while True:
new_cust = self.create_cust()
print("customer %s created at %d" % (new_cust.name, self.env.now))
yield self.env.timeout(self.period, new_cust)
The process created out of run
will emit an event every period
units of time.
The queue:
from __future__ import annotations
from simpy import Environment
from simpy.events import AnyOf, Event
from input_process import InputProcess
class Queue(object):
def __init__(self, env: Environment, period: float):
self.env = env
self.period = period
self.inputs = []
env.process(self.listen())
def add_input(self, node: InputProcess):
print("ADDED")
self.inputs.append(node.action)
def listen(self):
while True:
if len(self.inputs) > 0:
print("YES")
yield AnyOf(self.env, self.inputs)
print("TRIGGERED")
The simulation:
import simpy
from input_process import InputProcess
from queue import Queue
def run():
env = simpy.Environment()
gen = InputProcess(env, "T1", 1/2)
queue = Queue(env, 3)
queue.add_input(gen)
env.run(until=25)
if __name__ == "__main__":
run()
The output:
ADDED
traffic generator originated job 'T1-0' at 0
YES
traffic generator originated job 'T1-1' at 2
traffic generator originated job 'T1-2' at 4
traffic generator originated job 'T1-3' at 6
traffic generator originated job 'T1-4' at 8
traffic generator originated job 'T1-5' at 10
traffic generator originated job 'T1-6' at 12
traffic generator originated job 'T1-7' at 14
traffic generator originated job 'T1-8' at 16
traffic generator originated job 'T1-9' at 18
traffic generator originated job 'T1-10' at 20
traffic generator originated job 'T1-11' at 22
traffic generator originated job 'T1-12' at 24
As you can see, the queue tries to pause and wait for any input's events. In the current setting, there is only one input to the queue: the InputProcess
.
Even though the simpy process of InputProcess
does regularly yield events, the Queue
never moves away from the yield
, indicating that it keeps waiting for an event to be released by the InputProcess
, but that process does regularly creates events.
I am not understanding what is wrong here.
here is one thing that is happening.
in InputProcess(object): the init the line
self.action = env.process(self.run())
creates a process and assigns it to your action property.
A process runs until it hits a return statement or gets to the end of the method.
Your run() method does not have a return, and your loop is infinite, so run() never exits.
Another point: in the infinite loop of run(), you are creating a customer, creating a timeout() that you yield to, which means you wait till the timeout triggers/finish, then you loop. The customers and the timeouts never get saved anywhere.
So here is a example of a process generator and a process, no queue needed. This just demos one of many patterns for a simpy simulation
"""
Simple example of a process genterator
Programmer: Michael R. Gibbs
"""
import simpy
import random
def a_call_back(env, cust):
"""
shows how processes can call other stuff
This is just a normal function and not a simpy process
"""
print(f'{env.now} customer {cust} called a fuction')
def cust_proc(env, cust):
""""
Simple life of a customer
"""
print(f'{env.now} customer {cust} has started doing stuff')
# does some stuff
yield env.timeout(random.triangular(1,3,9))
print(f'{env.now} customer {cust} has finished doing stuff')
def gen_cust_processes(env):
"""
Creates customers and starts their process
"""
id = 0
while True:
id += 1
cust = 'Customer: ' + str(id)
# create a process, not no yield here
proc = env.process(cust_proc(env, cust))
# add call back, need lambda to customise
proc.callbacks.append((lambda e, c=cust: a_call_back(env, c)))
# add some time between creates
yield env.timeout(random.triangular(1,2,5))
# boot it up
env = simpy.Environment()
env.process(gen_cust_processes(env))
env.run(100)