Process Flow makes it easy to define customized fixed resources, which is one of the purposes of the Fixed Resource Process Flow. This tutorial will teach you how to implement a customized fixed resource that batches and processes three items at a time.
Model Description
The following image shows the 3D setup for this model.
We will implement the logic for the Batch3 object. Batch3 will perform the following sequence:
Batch3 will sequentially receive 3 items, processing them each for 5 seconds. Then it will process all 3 items for an additional 10 seconds before releasing them to the next object. Once all items have exited, it will start over on the next batch.
Concepts Covered
This tutorial will cover the following concepts:
- Creating a Fixed Resource Process Flow
- Creating and using the Schedule Source, Wait For Event, and Delay activities.
- Receiving and Releasing items in a BasicFR using Process Flow.
Build the Batching Processor
- Create the Fixed Resource Process Flow - Starting with a new model, go to the Toolbox, and click the button and select Process Flow > Fixed Resource. This will add a Fixed Resource Process Flow. Name it Batch3. Click back in the 3D view. You should now see in the Drag-Drop Library a new Process Flow category with the Batch3 Process Flow in it. This is an object that, when added to the model, will create a new instance of the Batch3 Process Flow. Each instance will execute the Batch3 Process Flow's logic.
Using the BasicFR
The Batch3 instances that you create are, by default, instances of the BasicFR class. The BasicFR is a "blank slate" fixed resource. The BasicFR can receive items through its input ports and sends items through its output ports, just like any other fixed resource. However, the logic defining when to receive and release items is left up to the user, hence it is a "blank slate" fixed resource. This is just what we need for this tutorial.
In some other cases you may not want to use the BasicFR as the default, but instead want to use something like a queue or processor. You can define that by pressing the Edit Instance Object button in the Process Flow's General Properties, or by explicitly attaching existing objects to the Process Flow. In this tutorial, however, we will use the default BasicFR.
- Create the 3D Layout - Create and connect the model 3D layout, namely a source, a queue, the Batch3 object, a processor, and a sink. Name the Batch3 instance Batch3 and resize it so that items in it won't be covered by its shape.
- Create the Process Flow Activities - Go back into the Batch3 Process Flow view. Create the Process Flow layout as shown in the following image.
This includes:
- - A Schedule Source.
- - 6 Wait for Events (3 to receive items, and 3 to release items).
- - 4 Delays (1 for each received item and 1 for processing all items together).
- Schedule Source Properties - By default, the Schedule Source creates a single token right when the simulation starts. This is exactly what we want to do in our logic, so we can leave it as it is.
- Receive/Process Activity Properties - For each of the 3 items, we want to, first, receive the item, and then process it for 5 seconds.
- Click on the Receive/Process Item 1 block, and look in Quick Properties, under Receive Item 1.
- The Object field should be left as its default of
current
. This means we want to listen to an event on the instance object, namely the Batch3 object in the 3D model. - For the Event field, press the button and then move the cursor over the Batch3 object in the 3D view. If the 3D view is behind a tab, hover over the 3D model tab to move to that tab, and then go to the Batch3 object. Click on the Batch3 object and select On Entry. This will make the activity listen for when an item enters the Batch3 object.
- Now you should see under Label Matching/Assignment a table with 2 rows. In the Entering Item row, enter item1 for the Label Name column, and select assign for the Operation column. What we are doing here is saying that, when the OnEntry event happens, we want the token's item1 label assigned to be a reference to the item that just entered as part of that OnEntry event. Later when we want to reference that item, we can use the token's item1 label.
- Next, under OnListenerInitialized, press the button and select Control > Basic FR > Receive Item. Make sure the trigger properties are as follows:
This will make the object call the receiveitem() command, which makes it available to receive an item from the upstream queue.
- Now go to the Process Item 1 properties. Under Delay Time, enter
5.00
.
The combined properties should look like the following:
Now implement the same properties on the other two Receive/Process blocks, except in those, define the Label Name as item2 and item3 respectively. This will make it so that each item will be assigned to a different label.
- Process/Release Activity Properties - Once we have received and processed each individual item, we want to process all of the items together for 10 seconds, then release each item.
- Click on the Process/Release block, and go to Quick Properties.
- Give Process All Items a Delay Time of
10.00
. - In Release Item 1, leave Object as
current
, and using the Event button, choose the On Exit event of the Batch3 object, just like we did for On Entry previously. - Under OnListenerInitialized press the button and select Control > Basic FR > Release Item. Define the item to release as
getlabel(token, "item1")
. The trigger properties should look like the following: - Do steps 3 and 4 again for the final two Release Item activities, except define the item to release as
getlabel(token, "item2")
andgetlabel(token, "item3")
respectively.
Run the Model
We can run the model now. Reset and run, watching the items in the 3D view and the tokens in the Process Flow.
Hey, It Doesn't Work!
First off, you may need to move the boxes around in the Batch3 to actually see all 3. When an item is moved into the Batch3, since we don't explicitly set its location, it will keep the same location that it had in the previous queue, except now in Batch3's coordinate space. Thus they may accumulate on top of each other. So, in the next phase we'll put in steps to set item location explicitly.
Second, and more importantly, you will notice that when the token gets to Release Item 2, it will actually release both items 2 and 3 at the same time and then jump to the beginning of the next batch. The processor then processes 2 items simultaneously. This is obviously the wrong behavior.
This is an artifact of the way FlexSim's event handling works. What is happening is that when the Processor is ready to receive its next item, it opens its input ports, which causes item 2 to exit Batch3. The OnExit is fired, which causes the next activity (Release Item 3) to be executed. Because all this functionality is triggered by the OnExit (which is called before the OnEntry of the processor) it all happens before the processor even knows that it received item 2 and can thus block further input. Consequently, item 3 is released and exits immediately as well, because it sees that the processor's input ports are still open.
This same problem can crop up in other scenarios. Within Process Flow it usually happens when a listened event is fired on a Wait For Event or Event-Triggered Source, and that is followed by one or more other activities that themselves may cause events in the model to fire, without any delays between the original triggered event and the subsequent event triggering logic. Affected logic may include operations like opening inputs or outputs, receiving or releasing items, moving objects, etc. The logic happens so fast that it doesn't give the original event enough time to "breathe" and finish what it was doing. In these cases, you can fix the problem by adding a Delay activity to delay for 0 seconds. This will allow the original triggering event to finish what it was doing before the token moves on. We will do this in the next phase of the model.
Let the Event-Triggering Object "Breathe"
If you have a Wait for Event or Event-Triggered Source that waits for an event and then immediately executes logic that may fire other events, it can be a good idea to put a 0 second delay after the event-waiting activity. This will allow the object that triggered the event to finish its event logic.
Adjust the Model
Now let's go back and make adjustments to our Process Flow. First, we'll put activities in to set the item locations, so we don't get items on top of each other. Second, we'll put 0 second delays after release item activities.
- Set Locations - For each of the 3 Receive/Process blocks, insert a Change Visual between the Receive Item and the Process Item steps. In their Quick Properties under Change Visual, press the button and select Set Location, Rotation or Size. Set the respective locations to (0, 0, 1), (1, 0, 1), and (2, 0, 1). Make sure that the activities set locations on the items referenced by labels item1, item2, and item3, respectively.
Below is the trigger properties for the first Change Visual
Alternatively, you can set the Z Location value tozsize(current)
. This will put the item right on top of the Batch3. - Add Delays - Add a Delay of 0 seconds after each of the Release Item activities.
Below is an image of the modified Process Flow.
Run the Model
Now we can run the model again. Reset and run the model. You should see the items being collected 3 at a time, processed, then released.
Receiving/Releasing vs. Open Outputs/Inputs
In this model we used the Receive Item and Release Item options for receiving and release items in the OnListenerInitialized fields. We use these because our base object is the BasicFR "blank slate" class. Remember that this object does not have its own logic for receiving and releasing objects, so we have to put that logic in, and we do that using the Receive Item and Release Item options. If we were instead attaching our Process Flow to other objects like queues and processors, those objects already have their own logic for receiving and releasing items, so we would in those cases use options that close/open input/output on those objects. This acts as an "overlay" on top of the standard object logic, where you close output to block any released items from actually leaving, etc.
Using Wait for Event to Receive/Release Items
It may seem a little counter-intuitive that we are using a Wait for Event to actually call the functionality that receives and releases items. A more intuitive logic might look more like the following.
Here you first release the item, and then you wait for the item to exit. The problem with this implementation, though, is that the releaseitem() command may actually cause the item to exit before the command is even finished executing. This means that by the time you get to the Wait for Item 1 to Leave step, item 1 will already have exited, so the event it's waiting for will have already fired, and won't fire again. The token would thus get stuck in the wait. So, to alleviate this issue, the Wait for Event gives you an OnListenerInitialized trigger. This allows you to first set up the listening, and then execute the logic that receives/releases the item. Thus you can do it all in one activity.