This article explores an example model. In this model, items on downstream lanes are able to reserve dogs so that items on upstream lanes cannot use them:
FlexSim simulates dogs on a power-and-free system in an extremely abstract and minimal way. A dog isn't a persistent entity at all. Instead, FlexSim calculates where dogs would be, given the speed, and when they would interact with items. This has a huge performance benefit. But if your logic needs items to interact with specific dogs, this can pose a problem: how do you interact with such an abstract entity?
The only time you can "see" a dog in FlexSim is during the Conveyor's Catch Condition:
The catch condition fires when a dog passes by an item. If the catch condition returns a 1, the item catches the dog and transfers to the power and free conveyor. If the catch condition returns a 0, the item does not catch the dog.
During the catch condition (and only during a catch condition), you can learn many things about a dog:
We'll use all these pieces of information in a moment.
The first real insight into this model is to make a dummy item. The purpose of this dummy item is to cause the Catch Condition to fire. It never gets on the conveyor. But when the catch condition fires, it makes a token that represents the dog. In this example, that item has a label called "DogFinder" Here is the relevant code from the catch condition:
if (item.DogFinder?) {
Object pe = current.DogPE;
if (pe.stats.state(1).value == PE_STATE_BLOCKED) {
return 0;
}
double dist = current.MaxDogDist;
double speed = current.targetSpeed;
double duration = dist / speed;
if (!item.labels["DistAlong"]) {
item.DistAlong = Vec3(item.getLocation(1, 0, 0).x, 0, 0).project(item.up, current).x;
}
Token token = Token.create(0, current.DogHandler);
token.DistAlong = item.DistAlong;
token.Conveyor = current.as(treenode);
token.Duration = duration;
token.DogNum = dogNum;
token.Speed = speed;
token.DetectTime = Model.time;
token.release(1);
return 0;
}There's a lot going on in this code:
Once the token is made, we need to push it to a list, so that items can pull them. If all your items are the same size, you can just push the token to a list directly. In this model, however, there are larger items that require two dogs. So there's a batch activity first. The dummy item is far enough back that it can detect two dogs and still push the first dog to the list in time for the first lane. So it holds the dog back in a Batch activity until one of two things happen:
If the batch is complete, the first dog in the batch can be marked as a "double", meaning the dog behind it is also available.
Once the flow has determined whether the dog is a single or double, it then pushes it to the list.
When pulling the dog from the list, an item needs to know the position of the dog relative to the item. Is it 0.3 meters upstream? Or is it 2 meters downstream? When we query the set of dogs, we need to filter out downstream dogs and order by upstream dogs, to reserve the closest one:
WHERE DistToDog >= 0 ORDER BY DistToDog
Here, DistToDog is positive if the dog is upstream, and negative if the dog is downstream. The code for this field is as follows:
/**Custom Code*/ Variant value = param(1); Variant puller = param(2); treenode entry = param(3); double pushTime = param(4); double distAlong = Vec3(puller.getLocation(1, 0, 0).x, 0, 0).project(puller.up, value.Conveyor).x; double dt = Model.time - value.DetectTime; double dx = value.Speed * dt; double dogDistAlong = value.DistAlong + dx; return distAlong - dogDistAlong;
This code assumes that the item waiting to merge is the puller. So we calculate the item's "dist along" the main conveyor. Then we estimate the location of the dog since the DogFinder item created the token. Then we can find the difference between the item's position and the dog's position.
Each incoming lane has a Decision Point. The main process flow creates a token when an item arrives there. At a high level, this token just needs to do something simple: pull an available downstream token. If all the items are the same size, it's that simple.
But this example is more complicated! If the item is large, we also need to pull the upstream dog behind the dog we got, so that no other item can get that dog.
And it gets even more complicated! It can happen that an item acquires the dog after a double dog. In that case, we need to mark the downstream dog as "not double", so that big items won't try to get it.
So most of the logic in the ConveyorLogic flow is handling that case.
Finally, the item must be assigned to that dog. The ConveyorLogic flow sets the DogNum label on the item. Then, the catch condition checks to see if the dog matches the item's DogNum.
The final piece of this model is allowing upstream items to catch a dog on this conveyor. This model adds a special label to those items called "ForceCatch". The catch condition always returns true for those items.
In this code stands “pe” for a self defined variable name of involved photo eye.