On a recent consulting project using the AutoMod simulation system, I encountered a situation in which it became necessary to select a load from an order list using a fairly complex set of criteria. While the general solution to this problem is very straightforward, it occurred to me that it requires an unusual—and, so far as I can tell, undocumented—syntax that I suspect will be unfamiliar to many AutoMod users. So, for the benefit of my fellows in the simulation community, I felt it appropriate to document this approach in my blog…
Say you have a set of loads, ranked in some particular order, waiting on an order list. A downstream process now needs to release one of these loads, but can’t simply take the the head load, if any, because whichever load is selected needs to satisfy a set of constraints. There is a simple clause—called satisfying—in the AutoMod order statement that allows you to filter the set of loads under consideration:
order a load satisfying condition from OL_someList to continue
where condition is a Boolean expression that must be logically true for a load on the list to be considered, and where OL_someList is the order list to be processed.
Most users are probably aware that they can use load attributes as part of the satisfying condition.
For example, say that you have a drilling machine whose tooling is configured to handle one particular type of load at a time. Load drill configurations are encoded as integer values, with the configuration required by each load stored in a load attribute named AI_drillConfig, and the drilling machine’s current configuration stored in the variable VI_currentDrillConfig. Loads sit in a buffer, while waiting for the machine, on an order list named OL_waitDrill. When it is ready to accept a new load, the machine attempts to release a load from the order list whose required drill configuration matches its current configuration. The following function demonstrates how this might be done:
begin FI_releaseDrillLoad function
order a load satisfying AI_drillConfig is VI_currentDrillConfig from
OL_waitDrill to continue
in case order not filled return false
return true
end
If there is a waiting load that requires the machine’s current configuration, then the first such load on the order list will be found and released and the function will return true. Otherwise, if there are no loads that require the machine’s current configuration, then the function will return false, possibly triggering a change to the machine’s configuration.
The approach demonstrated here is fine provided that all of the decision-making criteria reside in attributes on the loads in the order list. However, what if, say, our criteria are more demanding and reference information external to the loads on the list? For example, in our drilling example, what if we cannot simply select a matching load, but must select a matching load for which there is demand—discarding matching loads for which there is no demand?
This is a far more complex problem.
Before outlining how to solve the problem, let’s first go back to our existing function above and attempt to understand how AutoMod identifies the load to be released from the order list. The following is some equivalent pseudo-AutoMod code. (Note that this code will not compile, alas, because AutoMod does not permit an order list to be treated as though it was an object list. If you could do that, there would be no need for this blog article. However, it is useful to understand how the satisfying condition is applied.)
// NOT VALID AUTOMOD CODE!!!
begin FI_releaseDrillLoad function
for each drillLoad in OL_waitDrill do begin // CANNOT DO THIS!
if drillLoad AI_drillConfig is VI_currentDrillConfig then begin
// Load satisfies condition, so order off list, return true.
order load drillLoad from OL_waitDrill to continue
return true
end
end
// No satisfying load found.
return false
end
In this pseudo-code, drillLoad is a temporary LoadPtr variable; we’ll return to examine this in more detail shortly.
Now let’s refactor this psuedo-code to generalize the filter into a function—named FI_drillFilter—that takes a single LoadPtr argument and that returns true if it’s argument load can be released, or false if it cannot. We then populate the filter function with our original satisfying condition:
// NOT VALID AUTOMOD CODE!!!
begin FI_releaseDrillLoad function
for each drillLoad in OL_waitDrill do begin // CANNOT DO THIS!
if FI_drillFilter (drillLoad) <> false then begin
// Load satisfies condition(s), so order off list, return true.
order load drillLoad from OL_waitDrill to continue
return true
end
end
// No satisfying load found.
return false
end
begin FI_drillFilter /* argLoad */ function
if argLoad AI_drillConfig is VI_currentDrillConfig then begin
return true
end
return false
end
(If you’re wondering about the cumbersome-looking FI_drillFilter (drillLoad) <> false relation, see my earlier blog post Boolean Relations In AutoMod.)
It is now trivial to change the filter function to also verify that the argument load is demanded, through a function called FI_isLoadDemanded, which also takes a LoadPtr argument:
begin FI_drillFilter /* argLoad */ function
if argLoad AI_drillConfig is VI_currentDrillConfig and
FI_isLoadDemanded (argLoad) <> false then begin
return true
end
return false
end
(The implementation of the FI_isLoadDemanded function is not pertinent to this example. For brevity, we’ll assume that it exists and that it returns true if its argument load is in demand, or false if it is not.)
Now, all we need to do is to somehow refactor the FI_releaseDrillLoad function back into valid AutoMod code and we’re done.
But there’s a problem: how do we call the FI_drillFilter function as part of the satisfying clause of an order statement? In particular, how do we supply the LoadPtr argument—representing the load on the order list currently being examined—to the FI_drillFilter function?
There’s a clue to be found if we look closely at the original, working implementation of our FI_releaseDrillLoad function. Notice how the AI_drillConfig attribute is not prefixed by a LoadPtr value? How can that be? Under normal circumstances, if we do not specify such a value, AutoMod assumes that the attribute belongs to the current load (this load). In fact, there are two puzzling aspects to this:
- How can the current load represent loads stored in an order list?
- How can a function even have a current load? If you modify the FI_drillFilter function and remove the argLoad prefix to the AI_drillConfig attribute, you will get a parse error informing you that: Attributes are only allowed in a function following a vehicle or load pointer. Yet we do not get this error in the original version of FI_releaseDrillLoad.
AutoMod is clearly doing something a little unusual inside a satisfying clause. It appears to be supplying a LoadPtr—automatically, yet invisibly—for each load on the order list in turn. In our pseudo-code versions of FI_releaseDrillLoad, we named this invisible value drillLoad.
In fact, it turns out that when AutoMod encounters the expression this load within a satisfying condition—whether explicitly or implicitly—it interprets it to refer to the load on the order list currently being examined, rather than the current load. Consequently, it now becomes trivial to refactor FI_releaseDillLoad as desired:
begin FI_releaseDrillLoad function
order a load satisfying FI_drillFilter (this load) <> false from
OL_waitDrill to continue
in case order not filled return false
return true
end
begin FI_drillFilter /* argLoad */ function
if argLoad AI_drillConfig is VI_currentDrillConfig and
FI_isLoadDemanded (argLoad) <> false then begin
return true
end
return false
end
The expression this load in FI_releaseDrillLoad does not refer to the current load—it instead refers to each load on the order list OL_waitDrill in turn.
This approach can easily be adapted to address almost any order list selection filtering situation you might encounter:
- Write a filtering function that includes all of your selection criteria. Return true if a load meets your selection criteria or false if it does not.
- Call your filtering function, passing this load as an argument (plus any others you might wish to include), filtering out all loads for which this function returns false, as the satisfying condition of your order statement.
Hope this helps!
Mike Allen
President, Hindsight Consulting, Inc.
John Carson
Mike Allen