Letztes Mal habe ich den Mechanismus der diskreten Ereignissimulation und ihre grundlegendste Implementierungsmethode verstanden. Insbesondere wird ein Ereigniskalender (eine Liste von Ereignissen, die in der Reihenfolge des Auftretens angeordnet sind) beibehalten, Ereignisse von Anfang an einzeln extrahiert und die Verarbeitung entsprechend dem Typ ausgeführt. dort gab es. Im Beispielcode gibt es eine while-Schleife in der run ()
-Methode des Modells, und jedes Mal, wenn die Schleife umrundet wird, wird das erste Ereignis aus dem Ereigniskalender entfernt, der Typ wird durch die if-Anweisung bestätigt und entspricht dem Typ. Ich habe den Schritt der Anleitung zur Verarbeitung ausgeführt. Man kann sagen, dass diese Implementierungsmethode der naivste Codierungsstil ist, der einfach den Mechanismus der diskreten Ereignissimulation so wie er ist codiert (im Folgenden wird dieser Stil manchmal als ereignisorientiert bezeichnet).
Tatsächlich weist dieser ereignisorientierte Codierungsstil (mindestens) zwei Probleme auf. Das erste ist, dass die Methode "run ()" abhängig von der Anzahl der Ereignistypen und der Anzahl der Komponenten des Zielsystems größer wird. Zweitens, weil die Prozesse, die den Zustand des Zielsystems ändern, nach dem Signal gruppiert sind, das die Änderung verursacht, dh nach dem Ereignis, und nicht nach dem Subjekt, das die Änderung verursacht, oder nach dem Objekt, das die Änderung vornimmt, untereinander (aus der Perspektive des Subjekts oder des Objekts). Der Punkt ist, dass eine Reihe verwandter Änderungen in kleine Teile unterteilt und in mehreren verschiedenen Teilen beschrieben werden. All dies behindert den Ausblick des gesamten Codes, und ihre Auswirkungen werden mit zunehmender Größe des Zielsystems immer schwerwiegender.
Eine Möglichkeit, diese Probleme zu lösen, besteht darin, einen prozessorientierten Codierungsstil zu verwenden. Mit der Einführung des Python-SimPy-Moduls können Sie problemlos mit der Entwicklung in diesem prozessorientierten Stil fortfahren. Dieses Mal lernen wir die Grundlagen der Codierung der diskreten Ereignissimulation mit diesem Modul.
Als erstes müssen Sie das Simply-Modul installieren. Mit Google Colaboratoty können Sie die Installation einfach mithilfe von pip wie unten gezeigt durchführen (beachten Sie, dass Sie das! Am Anfang der Zeile bei der Installation in Ihrer eigenen lokalen Umgebung nicht benötigen).
! pip install simpy
Es scheint, dass die drei Hauptkomponenten von SimPy die Environment-Klasse in core.py, die Event-Klasse und ihre Unterklassen in events.py sowie die ressourcenbezogenen Klassen im Ressourcenverzeichnis sind. Ich sollte es tun. Darüber hinaus spielen als Generatoren implementierte Prozessfunktionen und -methoden eine wichtige Rolle. Ich werde das nächste Mal ressourcenbezogene Klassen diskutieren und dieses Mal die Grundlagen der prozessorientierten Codierung basierend auf den anderen drei behandeln.
Die Umgebungsklasse bietet die grundlegendsten Funktionen für die diskrete Ereignissimulation, z. B. das Verwalten der Simulationszeit und das Bearbeiten des Ereigniskalenders. Daher muss bei der Entwicklung eines Simulationsmodells mit SimPy eine Simulationsumgebung (= eine Instanz der Environment-Klasse) erstellt werden. Andererseits ist die Ereignisklasse eine Klasse zum Ausdrücken von Ereignissen, und einige Unterklassen werden ebenfalls vorbereitet, wie später zu sehen sein wird.
Das Registrieren eines bestimmten Ereignisses (= einer Instanz der Ereignisklasse) im Ereigniskalender wird als Auslösen bezeichnet. Im Fall eines normalen Ereignisklassenereignisses tritt es zum gleichen Zeitpunkt auf, zu dem es ausgelöst wurde. Andererseits möchten wir häufig Ereignisse verwenden, die nach einer vorgegebenen Zeitverzögerung nach dem Auslösen auftreten. Verwenden Sie in diesem Fall die Timeout-Klasse, die eine Unterklasse der Event-Klasse ist.
In SimPy wird der Prozess, der ausgeführt wird, wenn ein bestimmtes Ereignis eintritt, als Rückruf dieses Ereignisses bezeichnet. Durch Zuweisen einer Reihe von Rückrufen "e.callbacks" zu jedem Ereignis "e" tritt das Ereignis "e" auf. Die damit einhergehenden Prozesse werden gemeinsam ausgeführt. Schauen wir uns ein einfaches Beispiel an.
import random
import simpy
class Skelton:
def __init__(self, env):
self.env = env # pointer to the SimPy environment
self.count = 0 # an example state variable
def update(self, e):
self.count += 1 # increment the event counter
def print_state(self):
print('{} th event occurs at {}'.format(self.count, round(self.env.now)))
def run(self, horizon):
while True:
e = simpy.Timeout(self.env, random.expovariate(1)) # create an Timeout instance
e.callbacks.append(self.update) # register update() method in e's callbacks
if self.env.now > horizon: # if horizen is passed
break # stop simulation
else:
self.print_state()
self.env.step() # process the next event
env = simpy.Environment()
model = Skelton(env)
model.run(200)
In diesem Beispiel wird fast dieselbe Funktion wie beim vorherigen Skelettmodell mithilfe der Umgebungsklasse und der Timeout-Klasse von SimPy reproduziert. Die Ereignisklasse und die Kalenderklasse, die ich zuletzt erstellt habe, sind nicht erforderlich (da SimPy die entsprechenden Funktionen bereitstellt). Schauen Sie sich die letzten drei Zeilen an. Nach dem Erstellen der Simulationsumgebung (= env
) wird das Modell ( = model
) des Zielsystems unter Verwendung dieses Modells als Argument generiert. Dann wird die "run ()" -Methode dieses Modells mit "horizont = 200" ausgeführt.
Beim Überprüfen des Inhalts der Skelton-Klasse hat die run ()
-Methode eine while-Schleife, und jede Runde generiert ein Ereignis (= e
) der Timeout-Klasse und setzt ihre Rückrufe ( = e
). Sie können sehen, dass die Methode update ()
in .callbacks registriert ist
). Die Methode "update ()" ist ein Dummy, der nur "count" erhöht. Darüber hinaus muss der Rückruf eines Ereignisses in Form einer Funktion (genau genommen eines aufrufbaren Objekts) erfolgen, die das Ereignis als einziges Argument verwendet.
Das erste Argument beim Generieren eines Ereignisses der Timeout-Klasse ist die entsprechende Simulationsumgebung "env", und das zweite Argument ist die Länge der Zeitverzögerung (im obigen Beispiel wird sie durch eine Zufallszahl angegeben, die der Exponentialverteilung folgt). Beachten Sie, dass das Timeout-Ereignis automatisch ausgelöst wird, wenn es generiert wird (obwohl ein normales Ereignisklassenereignis wie später beschrieben explizit ausgelöst werden muss).
Die Simulationszeit wird von der Variablen "now" der Simulationsumgebung "env" verwaltet (sie kann von "self.env.now" aus der obigen Methode "run ()" referenziert werden). Wenn dieser Wert größer oder gleich dem als Argument übergebenen "Horizont" ist, wird die while-Schleife beendet und die Simulation beendet. Andernfalls rufen wir die Methode "step ()" der Simulationsumgebung auf, die das erste Ereignis "e" aus dem Ereigniskalender entnimmt und dessen Auftreten verursacht (dh es ist in "e.callbacks" enthalten. Dies entspricht dem Vorgang der Ausführung der vorhandenen Rückrufe in der angegebenen Reihenfolge.
Die Skelton-Klasse im obigen Beispiel ist erheblich einfacher als die vorherige, da einige ihrer Funktionen der Simulationsumgebung überlassen bleiben. Dies allein bedeutet jedoch, dass SimPy sich um die grundlegenden und allgemeinen Funktionen kümmert, sodass Sie weniger selbst programmieren müssen. In der Tat kann gesagt werden, dass der wesentliche Vorteil der Einführung von SimPy darüber hinaus liegt.
Prozessfunktionen und -methoden bewirken diesen wesentlichen Vorteil. Dies ermöglicht es SimPy, prozessorientiert zu codieren. Als nächstes erklären wir den grundlegenden Mechanismus anhand eines Beispiels. Siehe das folgende Beispiel.
class Skelton2:
def __init__(self, env):
self.env = env # pointer to the SimPy environment
self.count = 0 # an example state variable
def print_state(self):
print('{} th event occurs at {}'.format(self.count, round(self.env.now)))
def process_method(self): # an example process method
while True:
self.print_state()
yield simpy.Timeout(self.env, random.expovariate(1))
self.count += 1 # corresponding to Skelton's update()
def process_func(env): # an example process function
while True:
env.model.print_state()
yield simpy.Timeout(env, random.expovariate(1))
env.model.count += 1 # corresponding to Skelton's update()
env = simpy.Environment()
env.model = Skelton2(env)
# simpy.Process(env, process_func(env)) # when using process function
simpy.Process(env, env.model.process_method()) # when using process method
env.run(until=200)
Dies ist eine Umschreibung des obigen Beispiels unter Verwendung von Prozessfunktionen und -methoden. Möglicherweise haben Sie bemerkt, dass die Methode "run ()" (und die Methode "update ()") in der Skelton-Klasse verschwunden ist und eine neue Methode namens "process_method ()" in der Skelton2-Klasse angezeigt wurde. Dies ist die Prozessmethode. Beachten Sie, dass diese Prozessmethode möglicherweise nicht verwendet wird. Stattdessen kann eine Prozessfunktion verwendet werden, die dieselbe Funktion ausführt (im obigen Beispiel die Funktion process_func ()
) (beide werden in diesem Beispiel vorbereitet, aber in der Praxis). Nur einer von ihnen ist erforderlich).
Wie Sie den Yield-Anweisungen in "process_method ()" und "process_func ()" entnehmen können, handelt es sich um Python-Generatoren. Während normale Funktionen und Methoden ein Ergebnis zurückgeben und beenden, pausiert der Generator nur, wenn er ein Ergebnis mit Ausbeute zurückgibt, und wird nicht beendet. Wenn das Signal des Neustartbefehls später empfangen wird, wird die Verarbeitung an der Spitze der Yield-Anweisung neu gestartet.
Auf diese Weise sind Prozessfunktionen und -methoden Generatoren, die in Form von Instanzen der Event-Klasse definiert sind, und SimPy verwendet dies als Trick für die prozessorientierte Codierung. Insbesondere wenn eine Prozessfunktion / -methode ein Ereignis "e" ergibt, wird automatisch eine Neustartanweisung für diese Prozessfunktion / -methode zu "e.callbacks" hinzugefügt.
Prozessfunktionen / -methoden werden neu gestartet, wenn ein Ergebnis auftritt. Daher sollte die durch dieses Ereignis verursachte Statusänderung (in diesem Beispiel das Inkrement von "count") direkt im Teil nach dem Neustart beschrieben werden. werden. Daher ist es in diesem Beispiel nicht mehr erforderlich, die Methode "update ()" in der Gruppe der Rückrufe zu registrieren. Wie in diesem Beispiel kann es schwierig sein, die Vorteile eines einzelnen Timeout-Ereignisses und einer einfachen Statusänderung ("count" -Inkrement) zu realisieren, aber die Statusänderung verläuft auf komplizierte Weise, während sie von mehreren Ereignissen beeinflusst wird. Dies ermöglicht es, jeden Prozess intuitiv zu beschreiben.
Damit die erstellte Prozessfunktion / -methode in der Simulation ausgeführt werden kann, muss sie in der Simulationsumgebung registriert sein. Dies erfolgt in der zweiten Zeile von unten (und in der dritten Zeile auskommentiert). Insbesondere können Sie sehen, dass eine Instanz der Process-Klasse erstellt wird. Zu diesem Zeitpunkt wird der Prozess des Erzeugens eines Ereignisses (Ereignis initialisieren), das ein Signal zum Starten des entsprechenden Prozesses und zum Auslösen des Ereignisses aussendet, automatisch hinter den Kulissen ausgeführt.
Außerdem ist die Methode "run ()" der Simulationsumgebung in der unteren Zeile ein Wrapper, der die Methode "step ()" wiederholt. Als "Ausführen (bis = Zeit)" oder "Ausführen (bis = Ereignis)" kann die Simulation bis zu einer bestimmten Zeit oder bis zu einem bestimmten Ereignis fortgesetzt werden. In diesem Beispiel wird die Simulation fortgesetzt, bis die Simulationszeit 200 erreicht.
Sie können mehrere Prozessfunktionen / -methoden definieren und in derselben Simulation ausführen, während Sie sie miteinander verknüpfen. Schauen wir uns hier ein Beispiel an. Ein einfaches Beispiel ist unten gezeigt.
class Skelton3(Skelton):
def __init__(self, env):
super().__init__(env)
def main_process(self):
while True:
self.print_state()
yield self.env.timeout(random.expovariate(1)) # shortcut for simpy.Timeout()
self.count += 1
if self.count %3 == 0:
self.env.signal4A.succeed() # signal for resuming sub process A
def sub_process_A(self):
self.env.signal4A = self.env.event() # create the first signal
while True:
yield self.env.signal4A
print('> sub process A is resumed at {}'.format(round(self.env.now)))
self.env.signal4A = self.env.event() # create the next signal
if self.count %5 == 0:
self.env.process(self.sub_process_B()) # register sub process B
def sub_process_B(self):
print('>> sub process B is started at {}'.format(round(self.env.now)))
yield self.env.timeout(10) # shortcut for simpy.Timeout()
print('>> sub process B is finished at {}'.format(round(self.env.now)))
env = simpy.Environment()
env.model = Skelton3(env)
env.process(env.model.main_process()) # shortcut for simpy.Process()
env.process(env.model.sub_process_A()) # shortcut for simpy.Process()
env.run(until=200)
In der Skelton3-Klasse sind drei Prozessmethoden definiert: "main_process ()", "sub_pricess_A ()" und "sub_process_B ()". Von diesen ist die Methode "main_process ()" bis auf die letzten beiden Zeilen fast dieselbe wie die Methode "process_method ()" der Skelton2-Klasse. Die Methode "timeout ()" in der Simulationsumgebung ist eine Verknüpfung zu "simpy.Timeout ()" und wird häufig verwendet, da nur ein Argument erforderlich ist.
In den letzten beiden hinzugefügten Zeilen können Sie sehen, dass ein bestimmter Prozess ausgeführt wird, wenn der Wert von "count" durch 3 teilbar ist. Hier ist "signal4A" in der Simulationsumgebung eine Instanz der Ereignisklasse, die in der ersten Zeile (und fünften Zeile) der Methode "sub_process_A ()" generiert wird, dh ein Ereignis. Und die Methode success ()
des Ereignisses führt den Prozess des Auslösens aus. Daher erfüllt dieser Teil die Funktion, jedes Mal, wenn "count" durch 3 teilbar ist, "signal4A" auszulösen.
Schauen Sie sich als nächstes die Methode "sub_process_A ()" an. Da dieses Ereignis in der dritten Zeile angezeigt wird, wird diese Methode an dieser Stelle angehalten. Dann wird "signal4A" durch die Methode "main_process ()" ausgelöst, und wenn die Simulationsumgebung dieses Ereignis verursacht, wird die Methode "sub_process_A ()" neu gestartet. Dieser Ablauf ist eine der typischen Methoden zum Zuordnen mehrerer Prozessfunktionen / -methoden.
Wenn Sie sich die zweite und dritte Zeile am Ende des gesamten Codes ansehen, sehen Sie, dass sowohl die Methode main_process () als auch die Methode sub_process_A () vor dem Start der Simulation in der Simulationsumgebung registriert sind. Die Methode process () in der Simulationsumgebung ist eine Verknüpfung zu simpy.Process (), die auch häufig verwendet wird, da nur ein Argument erforderlich ist.
Wenn die Simulation gestartet wird, werden diese Prozesse daher automatisch gestartet und gemäß der oben definierten Interaktion fortgesetzt (insbesondere wird die Methode "main_process ()" zuerst gestartet und dann ausgegeben. Nach dem Fortfahren und Anhalten wird die Methode "sub_process_A ()" gestartet, fährt fort zu pausieren und wird angehalten. Danach wird, wenn ein Timeout-Ereignis auftritt, die Methode "main_process ()" neu gestartet, in der das "signal4A" " Wenn dies auftritt (dann wird die Methode "main_process ()" angehalten), wird die Methode "sub_process_A ()" neu gestartet und so weiter).
Als nächstes schauen wir uns die Methode "sub_process_B ()" an. Es ist ersichtlich, dass dies ein One-Shot-Prozess ist, der keine while-Schleife hat. Wie wird die Ausführung dieses Prozesses gesteuert? Tatsächlich ist das Rätsel in der Methode "sub_process_A ()" verborgen. Schauen Sie sich die letzten beiden Zeilen an. Wenn count
durch 5 teilbar ist, können Sie sehen, dass die Methode sub_process_B ()
in der Simulationsumgebung registriert ist. In Reaktion darauf wird dieser Prozess automatisch ausgeführt. Auf diese Weise kann die Registrierung eines neuen Prozesses in der Simulationsumgebung nicht nur vor dem Start der Simulation, sondern auch jederzeit nach dem Start der Simulation durchgeführt werden. Dieser Ablauf ist auch eine der typischen Methoden zum Zuordnen mehrerer Prozessfunktionen / -methoden.
Das Ereignis "e" hat eine Variable namens "Wert". Der Standardwert von "e.value" ist "None", aber Sie können ihn auf einen anderen Wert (außer "None") setzen und an die Prozessfunktion / -methode übergeben. Dazu beim Auslösen des Ereignisses e
e.succeed(Der Wert, den Sie als Wert festlegen möchten)
(Geben Sie im Fall eines Timeout-Ereignisses beim Erstellen einer Instanz als Schlüsselwortargument " value = value, den Sie auf value
setzen möchten "an.) Dann auf der Seite der Prozessfunktion / Methode im Ertragsteil,
v = yied e
Wenn Sie schreiben, wird der Wert von "e.value" in "v" eingegeben.
Darüber hinaus hat das Ereignis "e" auch eine Variable namens "ok". Wenn beim Auslösen des Ereignisses "e" die Methode "success ()" verwendet wird, wird automatisch "e.ok = True" gesetzt. Dies bedeutet, dass das Ereignis erfolgreich aufgetreten ist, wie Sie am Namen der Methode "success ()" sehen können.
Tatsächlich können Sie auch die Methoden "e.fail (Ausnahme)" und "e.trigger (Ereignis)" verwenden, um das Ereignis "e" auszulösen. Im ersteren Fall "e.ok = False", was darauf hindeutet, dass das Auftreten des Ereignisses auf irgendeine Weise fehlgeschlagen ist. Wenn diese Methode verwendet wird, wird die in "Ausnahme" angegebene Ausnahme in "e.Wert" eingegeben, und die Ausnahme tritt auf, wenn das Ereignis "e" verarbeitet wird (also die Warteprozessfunktion / Die Ausnahmeverarbeitung erfolgt nach einer Methode usw.). In letzterem Fall werden die Werte von "ok" und "value" des Ereignisses "e" so festgelegt, dass sie mit denen eines anderen Ereignisses "event" übereinstimmen, das als Argument übergeben wird.
Es ist möglich, auf die logische Verbindung mehrerer Ereignisse mit Prozessfunktionen und -methoden zu warten. Verwenden Sie in diesem Fall "&" für und Kombination und "|" für oder Kombination. Zum Beispiel, wenn es drei Ereignisse gibt: "e1", "e2", "e3"
values = yield (e1 | e2) & e3
Es bedeutet, dass es so gemacht werden kann. Zu diesem Zeitpunkt wird values
zum OrderedDict of value
jedes Ereignisses (wenn der Wert von value
jedes Ereignisses nicht erforderlich ist, müssen natürlich" values = `" nicht geschrieben werden).
Umgekehrt kann auf dasselbe Ereignis von mehreren Prozessfunktionen / -methoden gewartet werden. In diesem Fall werden diese Prozesse in der Reihenfolge neu gestartet, in der die Neustartanweisungen (automatisch) zu den Rückrufen für dieses Ereignis hinzugefügt werden.
Bei der Registrierung von Prozessfunktionen und -methoden wurde eine Instanz der Process-Klasse erstellt. Dies,
p = simpy.Process(env, process_func())
Es kann zweckmäßig sein, später darauf zurückgreifen zu können, wie z.
Da die Process-Klasse von der Event-Klasse erbt, kann dies auch als Ereignistyp angesehen werden. Das heißt, das obige "p" kann als Ereignis behandelt werden (es wird bei der Rückgabe als ausgelöst betrachtet, und wenn es einen Rückgabewert gibt, wird es zum Wert von "Wert").
Durch Aufrufen der Methode 'interrupt ()' vor dem Auslösen des Ereignisses 'p' kann die entsprechende Prozessfunktion / -methode unterbrochen werden (abnormale Beendigung). Infolgedessen wird der entsprechende Neustartbefehl aus dem Satz von Rückrufen für das Ereignis "e" gelöscht, auf das die Prozessfunktion / -methode in Ausbeute wartet. Da die Ausnahme "simpy.exceptions.Interrupt (Ursache)" in diese Prozessfunktion / -methode geworfen wird, kann das Verhalten zum Zeitpunkt einer abnormalen Beendigung durch Empfangen und Verarbeiten angegeben werden. Diese Interrupt ()
-Methode wirkt sich nicht auf das Ereignis e selbst aus (daher können Sie nach der Behandlung der Ausnahme erneut auf das Ereignis e
warten).
Um Ihnen ein konkreteres Bild zu geben, möchte ich Ihnen ein Beispiel für eine einfache Bestandsverwaltung geben, die ich beim letzten Mal behandelt habe.
class Model:
def __init__(self, env, op, oq, lt, init):
self.env = env
self.op = op # ordering point
self.oq = oq # order quantity
self.lt = lt # replenishment lead time
self.at_hand = init # how many items you have at hand
self.loss = 0 # opportunity loss
self.orders = [] # list of back orders
@property
def total(self):
return sum(self.orders) +self.at_hand
def print_state(self):
print('[{}] current level: {}, back order: {}, lost sales: {} '.format(round(self.env.now), self.at_hand, self.orders, self.loss))
self.env.log.extend()
def seller(self):
while True:
yield self.env.timeout(random.expovariate(1))
if self.at_hand > 0:
self.at_hand -= 1 # sell an item to the customer
self.env.stocktake.succeed() # activate the stocktaker
else:
self.loss += 1 # sorry we are out of stock
self.print_state() # state after dealing with each customer
def stocktaker(self):
self.env.stocktake = self.env.event() # create the first signal
while True:
yield self.env.stocktake
if self.total <= self.op:
self.orders.append(self.oq)
self.env.process(self.deliverer()) # activate deliverer
self.env.stocktake = self.env.event() # create the next signal
def deliverer(self):
self.print_state() # state after an order is placed
yield self.env.timeout(self.lt)
if len(self.orders) > 0:
self.at_hand += self.orders.pop(0)
self.print_state() # state after an order is fulfilled
Im Vergleich zur vorherigen Modellklasse können Sie feststellen, dass die Methode run ()
(und einige andere Methoden) entfernt und drei neue Prozessmethoden definiert wurden. Diese Prozessmethoden gelten für den Verkäufer, der dem zufällig eintreffenden Kunden entspricht, den Lagerverwalter, der den Lagerbestand des Geschäfts überprüft und bei Bedarf eine Bestellung aufgibt, und den Zusteller, der die Bestellung erhält und das Produkt liefert. Es entspricht jeder Arbeit. Im Vergleich zur vorherigen Methode "run ()", bei der diese Funktionen auf gemischte Weise beschrieben wurden, scheint sich die Sichtbarkeit des Codes verbessert zu haben. Dieser Effekt nimmt mit der Größe des Zielsystems zu.
Nehmen wir einige Änderungen an der Log-Klasse gemäß der Einführung von SImPy vor.
import matplotlib.pyplot as plt
class Log:
def __init__(self, env):
self.env = env
self.time = []
self.at_hand = []
self.loss = []
self.total = []
self.extend()
def extend(self):
self.time.append(self.env.now)
self.at_hand.append(self.env.model.at_hand)
self.loss.append(self.env.model.loss)
self.total.append(self.env.model.total)
def plot_log(self):
plt.plot(self.time, self.at_hand, drawstyle = "steps-post")
plt.xlabel("time (minute)")
plt.ylabel("number of items")
plt.show()
Gehen Sie wie folgt vor, um dieses Simulationsmodell auszuführen:
env = simpy.Environment()
env.model = Model(env, 10, 20, 10, 20) # op, oq, lt, init
env.log = Log(env)
env.process(env.model.seller())
env.process(env.model.stocktaker())
env.run(until=200)
env.log.plot_log()
Schreiben wir das Simulationsmodell, das den Zustand der Mittagszeit in einem in der vorherigen Übung erstellten Restaurant ausdrückt, mit SImPy in prozessorientierten Code um.
Dieses Mal haben wir SimPy vorgestellt und die Grundlagen zum Erstellen eines prozessorientierten Simulationsmodells eingeführt. Schauen wir uns das nächste Mal die ressourcenbezogenen Klassen und ihre Verwendung an.
Recommended Posts