A Simple Discrete-Event Simulation: Part 9

It’s one thing when you place an entity back in the future events queue at a specific absolute or relative time, but it’s quite another if you want an entity to become active when a certain condition–or set of conditions–becomes true. Some way has to be found to represent all of the conditions that are causing wait states. Since any operation or any object may affect the value of one or more variables, and since there is no way to tell ahead of time which variables may be affected, the conditions have to be checked every time something happens.

The trick is to figure out what that something should be.

Here’s the first part of the solution. We’re going to start by creating a mechanism to store and process the condition checks in an organized way. Every item in this mechanism will be checked every time a new event is pulled from the future events queue. Every event pulled from the future event updates the current time, so we’re going to refer to the mechanism as the current events queue. Its structure, elements, and behavior are going to look very much like the future events queue in that it will be composed of a list of current event items, each of which is owned by an entity being modeled in the simulation. Here are the basic declarations.

You’ll notice that each current event item calls its parent’s checkBlockingCondition item, which implies that every modeled entity that wants to test for (one or more) wait conditions needs to implement a version of this function.

If you can imagine writing a statement of the form

you can think of the / statement as being replaced by whatever goes on in the checkBlockingCondition method. Since we don’t control the interpreter we have to create some external mechanisms as we are here.

Since we’re going to process the entire list of current event items every time we pull the next future event item we modify the main activity loop as follows.

Before continuing I’ll observe that this first implementation raises some issues. The way this is set up we make one pass through the current events queue, processing every event whose conditions are met. If we have a large number of conditions to check, which means a large number of variables may be changed, what is to say that a condition might become true and then untrue again before the relevant current event item is processed again? If it happens within the course of processing a single item it might not be a big deal (and is frankly unlikely to happen anyway), but if it happens across the processing of multiple items it might be a big deal, indeed. Perhaps ceq.processCeq() needs to be exited and called anew each time an item’s condition is found to be true. There are a lot of ways to do this but I’m going to park this thought for the moment. First we want to get something working.

To that end, here is an entity declaration that implements a conditional wait state. It’s been structured to allow for many different wait conditions, though only one can ever be active at one time.

What this entity actually does is trivial, and there are many simpler ways to do it. This happens to be the one I chose for testing. Here is the setup of the globalExecutionCount mechanism.

What happens is that the other entities call bumpGlobalExecutionCount() every time they are pulled off the future events queue, incrementing the global counter. When the current events queue is processed, before pulling the next event, the waiting entity tests to see if the global counter is evenly divisible by five, and writes out a message when it is.

The program now generates the following output:

104 minutes

entity 1 created at time 0
entity 2 created at time 0
entity 3 created at time 0
entity 4 created at time 0
entity 4 reports at time 0 executions: 0
entity 1 updated at time 11
entity 3 updated at time 12 position: -40
entity 2 updated at time 13
entity 3 updated at time 19.009999999999998 position: -35
entity 1 updated at time 21
entity 2 updated at time 26
entity 3 updated at time 26.019999999999996 position: -30
entity 1 updated at time 31
entity 3 updated at time 33.029999999999994 position: -25
entity 2 updated at time 39
entity 3 updated at time 40.03999999999999 position: -20
entity 1 updated at time 41
entity 3 updated at time 47.04999999999999 position: -15
entity 1 updated at time 51
entity 2 updated at time 52
entity 3 updated at time 54.05999999999999 position: -10
entity 1 updated at time 61
entity 3 updated at time 61.069999999999986 position: -5
entity 2 updated at time 65
entity 3 waiting at time 68.07999999999998 wait count: 4
entity 1 updated at time 71
entity 3 waiting at time 75.08999999999999 wait count: 3
entity 2 updated at time 78
entity 1 updated at time 81
entity 3 waiting at time 82.1 wait count: 2
entity 3 waiting at time 89.11 wait count: 1
entity 2 updated at time 91
entity 1 updated at time 91
entity 3 waiting at time 96.12 wait count: 0
entity 1 terminated at time 101
entity 3 terminated at time 103.13000000000001
entity 2 terminated at time 104

Here we see that the current events list is scanned as expected–once–when the global count is zero–whereupon the item takes itself out of the current events queue. Once that happens there’s nothing left to repeat the test.

This is actually what I intended. wait…until conditions are supposed to only happen once. When the condition becomes true, the modeled entity goes back to or on to some other behavior, which may be governed by bumping forward in time on the future events queue or waiting for a new condition on the current events queue. Before long I’ll come up with a better demonstration case, but the basic mechanism appears to work as far as it’s been tested.

I’ll also note that it’s possible to have multiple mechanisms in place simultaneously. Each modeled entity can create multiple current end future events queue items at the same time, and can be doing one thing while waiting for another. The SLX simulation language allows for different subentities it calls pucks (like a hockey puck). Each modeled entity can spawn multiple subentities, each of which can implement arbitrarily complex behavior. We’ll get into some examples of this as we go.

A similar thing happens in business process systems. An entity may enter a process and proceed through many stages, sometimes kicking off multiple subprocesses. Imagine that an order for a widget comes in. Different “pucks” may have to be sent to create a new customer or look up and existing one, pull items from inventory, do something in manufacturing or shipping, send a bill, wait for payment, pursue payment if it is not received by the expected date, and handle questions, change orders, clarifications, and cancellations. The BPMN standard discusses the diagramming and documentation of these ideas, but it is up to different business tools to implement them in different ways.

This entry was posted in Simulation and tagged , . Bookmark the permalink.

Leave a Reply