Friday, March 13, 2009

Wait Until Event

A lot of times in modeling a system in QUEST there comes a need for some synchronization between different elements. For instance, one model I made had a large piece removed from the main assembly part, which was stored until the main assembly part arrived at a specified machine. Once that assembly part leaves for the machine, the large part should start being delivered to the machine (while the machine is running a process to prepare the assembly part for installation of the big part).
When I first started with QUEST, I probably would have written some logic on the storage buffer holding my part to look at the machine at a specified time interval to see if the main assembly part had arrived yet. You can set the time interval to whatever you want, and there are drawbacks no matter what. If you specify a small interval (like 5 seconds), your model will slow down to a crawl because your logic is running more or less constantly. Add multiple elements running this logic and you can kiss model performance goodbye.
So what if you specify a larger interval? Again, you're still going to be running your logic a lot more than you need to, and with the larger interval you run the risk of not noticing the main assembly part arriving until too late.
There are a few ways we can address this problem without using a time interval to constantly keep checking our machine. Some would suggest using signals, where our machine would send a signal to our buffer saying the part is there, and it's time to route the part on to the machine. This will have good performance in the model, as the logic in the buffer just uses a WAIT UNTIL SIGNAL SCL command to wait.
What I don't like about this is the need to write logic for the machine to use to send the signal to the buffer. And what if we can't be sure what buffer is holding our part? Then our logic has to search for the part and alert that buffer using a signal. This is all doable, but I prefer to use (what I think is) a simpler approach: the WAIT UNTIL EVENT SCL command.
The WAIT UNTIL EVENT command would be used similar to how we would use the WAIT UNTIL SIGNAL command, except that QUEST is raising the event for us, so no logic needed anywhere except on the buffer routing the part.
To use the WAIT UNTIL EVENT command, you simply provide an event constant, and optionally a pointer to the element/element class you want to watch, as well as whether you want your logic to run right before the event, or right after. There are some other options (you can look for events on a part/part class, or segment).
So for the example above with the assembly part and the part that needs to move to the machine, our buffer would just have a loop that goes something like this:
while( true ) do
WAIT UNTIL EVENT PART_XFER FOR ELEMENT_CLASS the_elem_handle
-- look at the_elem_handle's inparts to see if our assembly part is there...
endwhile
and then our logic can continue, and route the part out of the buffer to the waiting machine. One thing to note here, too, is that to use the PART_XFER constant you must have #include at the top of your SCL file or your logics won't know what PART_XFER is and it won't compile. You could just look up the numeric codes for the event constants if you'd prefer, but the code is easier to read (especially after a year without looking at or thinking about that particular logic) and more future proof if you use the constant names.
This is just one example where we can use the WAIT UNTIL EVENT SCL command in place of using signals. Your mileage may vary, in that the events we want may not be exposed for use in the WAIT UNTIL EVENT command (for instance, when a part actually arrives at an element). In these cases you may need to use signals, but hopefully you'll be able to use WAIT UNTIL EVENT just as effectively.

Monday, March 2, 2009

How to run BCL from Excel (the simple way) Part 2

In the previous post, I showed you how to set up a batch file that launches QUEST with the path to a BCL file passed as the command line parameter.  This post will bring us the rest of the way to being able to save out a series of BCL commands generated in Excel out to a BCL file.

The easiest way to create the Excel portion of this setup is using a VBA-based Excel Addin (.xla file).  The XLA, once activated by the user, is available whenever Excel is running, and we can build toolbar buttons to execute the macros we're using.

The macros themselves are pretty straightforward if you're accustomed to writing VBA in Excel.  The VBA Project is unlocked and unprotected, so you can go in and start editing/hacking away as you'd like to.

Download the XLA file here.  Understand that it's intended just for demonstration purposes and comes with no warranty.

To install the Addin, save the XLA file to your computer and note its location.  Then start Excel and go to Tools->Add-Ins.  Select the "Browse" button and navigate to the XLA file, wherever you saved it, and that's it.  The Addin will open and you'll see a new toolbar (named BCL_Launcher_Toolbar) with a pair of buttons on it.

To use the buttons, first select the cells containing the commands you want to save/execute.  The button on the left will just save the commands to a text file, while the button on the right saves the commands and then launches QUEST and executes those commands.

The first time you use the Addin, you'll be prompted for the location of the BCL file where you want your commands saved to, as well as the location of your QUEST BCL BAT file that you created in the previous post.  After that you should be good to go.

How to run BCL from Excel (the simple way) Part 1

For some time now I've been building QUEST models by using BCL script rather than the traditional User Interface.  I do this because it provides me with a very detailed (and procedural) look at what I've done to a model, and helps to glean out what assumptions I've made along the way.

It also lets me rebuild a model from scratch whenever I want, so I can (somewhat) easily modify some model parameter and have that change made in my model.  To make changing parameters easier I build my BCL scripts in Excel using formulas.  When I need to rebuild my model I can just click a user button in Excel and up pops QUEST to execute my script.

The following posts will tell you how to set up the same thing in your version of Excel.

Since we're basically just going to build a text (.bcl) file in Excel and execute it in QUEST, we need to first set up a QUEST .bat file that will open QUEST and execute the commands in a BCL file.  Looking at the online documentation, all we have to do is append "-b" on to the call to QUEST.exe with the BCL file name.

To set up a quest_bcl.bat file just make a copy of your quest.bat file (or whatever .bat file you use to initialize environment variables before starting QUEST, usually Deneb\Quest\Quest.bat) and find the line where QUEST is being started (I believe it's something like "start/max %DENEB_PRODUCT%.exe %1 %2..."), and replace it with this:

quest -b < %1

The "< %1" just tells the command line to take the first command line argument passed in and pass it on to "quest -b".

To test this, try creating a simple BCL file that creates a machine and places it at some location, then transfers control of the program to the user (otherwise QUEST will just close out having completed its task):

    CREATE MACHINE CLASS 'Machine1'
    LOCATE ELEMENT 'Machine1_1' AT 100, 100, 100
    TRANSFER TO MENU

Save this into a plain text file and name it what you'd like (preferably with a .bcl extension, but it doesn't matter much).  Note the full path to the file.

Next, open a command prompt and navigate it to the directory where you saved your quest_bcl.bat file.  In the command prompt type: 

    quest_bcl.bat "bcl_file_path.bcl"

where bcl_file_path is the full path to the BCL file you saved (unless it's in the same directory as your quest_bcl.bat).  You don't have to enclose the BCL file path in double quotes unless there is a space in the path.

If everything is working right, QUEST should launch and create a machine at the specified coordinates.

In the next post, I'll show you how to set up a simple Excel Addin (.xla file) that will let you build a BCL file from a set of Excel cells and launch it in QUEST.

Friday, February 20, 2009

Select a part class

Since I've already posted how to select an element and a process using SCL, and because there was recently a question regarding this in the QUEST Google Group, I figured I'd post code for selecting a part class.

All this code does is populate an array wtih the names of all the part classes in the current model, then passes that to a list_pop_up, which returns the index in the array of the selected item.  The list_pop_up routine returns a boolean integer telling whether or not something was actually selected (eg the user didn't hit cancel).  So if this return value is true then just return the part class referenced in the array...

routine select_a_pclass() : Part_Class
Const
   max_pclass 250
Var

   result , the_pclass : Part_Class
   num_pclasses_found , pick_process , list_picked : Integer
   the_pclasses : Array[ max_pclass ] of String

Begin

   num_pclasses_found = 0
   the_pclass = first_pclass
   while( the_pclass <> NULL ) do

      the_pclasses[ num_pclasses_found ] = the_pclass->name
      num_pclasses_found = num_pclasses_found + 1
         
      the_pclass = the_pclass->next_pclass

   endwhile

   --do a list popup using the_pclasses
   list_picked = list_pop_up( 'Select a part class' , the_pclasses , pick_process , true )

   if( list_picked ) then
      return get_part_class( the_pclasses[ pick_process ] )
   endif

End

Friday, January 16, 2009

How Filehamster Saved My Life

I don't know about you, but in the course of developing SCL macros and logics I sometimes tend to make bad choices, and undoing those changes can be pretty painful, especially if I went and modified something on a large scale (i.e. adding a call stack on 328 subroutines in an SCL file and finding out that my implementation gets extremely slow as the stack grows).

Thankfully, for the past couple years I've used a tool called FileHamster from MOGWare, which is a free and extremely easy to use backup program that basically sits and watches any files you want it to, and when you change it it snags a copy and stores it for you.  You can add comments to a revision as it's copied or later on (like "Last backup before adding call stack") so you can easily identify the point where you made a change you need to reverse, and restore your file to that version.

Filehamster just takes a copy of what's changed and saves it out for you to a backup directory. But there's very little overhead in setting up a file watch, and with SCL files we're dealing with text files in the size range of 1-300K, so it would take a while to use up an appreciable amount of hard disk space.  If you're that concerned there's a plugin for sale that'll compress each revision for you.

There's actually a set of plugins for sale on their site for extending FileHamster into something more like a full-on source control package, but I'm going to hold out until they come up with a plugin that can hear that voice in the back of my head asking "does he really think this is going to work?"

Tuesday, January 13, 2009

SCL Call Stack

If you saw my last post on compiling SCL files from SCL, you may have seen that there are some array utility routines I've written that allow you to insert and remove array elements through a simple call providing the array and the number of elements in it.

After creating these routines, I thought they'd be perfect for implementing a call stack in SCL.  Having a call stack available in SCL would mean we can more easily track down the source of bugs in our scl subroutines, especially if they're more generic utilities that may be called from any number of different routines.

So the way I'll be using this is in a very large (8,000+ line) SCL utility where many routines are simply wrappers around BCL commands that make it so I don't have to write out BCL statements again and again.  Basically any time a subroutine starts or ends I just write the stack out to a text file; and if the macro crashes, I just check the latest stack text file and see what subroutines were in the stack, so I can track the problem down.


I'd suggest you save the call_stack.scl file into QUESTlib/UserDef/Logics so it's always compiled and ready to go when you need it...

Basically there are three procedures in the call_stack.scl file you need to worry about: start_procedure, end_procedure, and print_stack.  Just call start_procedure and end_procedure with the name of your subroutine as an argument, and the stack will reflect all subroutines you've got running.

However, like I said earlier, I have 8000 lines of SCL code with over 300 subroutines.  So how am I supposed to put in a call to start_procedure and end_procedure for all those subroutines?

The answer is using SCL, what else?  So I went and wrote an SCL macro that lets me select an SCL file, and it then goes through and identifies each subroutine in the file, and adds a call to start_procedure at the beginning and end_procedure at the end of each subroutine, with the name of the sub as the argument.  The file gets saved out as the original filename with "_cs" appended on, so you can go and copy it over if you want, or just run that for debugging purposes.

Monday, January 5, 2009

Model Scaling

I recently built a QUEST model on a CAD layout and it turned out the layout wasn't properly scaled.  This is easy enough to fix, just a matter of either modifying the CAD part in the QUEST drawing world, or just scaling it in the dimensions section of the layout accessory.

But what about the elements?  I had to scale the layout to about 120% of its original size and didn't feel like moving all the machines and buffers and everything to their new locations on the layout.

So what would I do other than write an SCL macro that lets me move all elements in a model relative to some new scale.  Here's the code.

Basically what it does is populates a spot structure, multiplies the location coordinates by the scale input by the user, and locates the element on the spot's new coordinates.

The best part about writing this macro was that it took about 5 minutes, just by copying in SCL subroutines I've previously written and was able to find using my SCL Subroutine Indexer.

Update: When using this macro I found it might be better to be able to scale the model differently along various axes.  So I went and modified the macro to scale each axis to different values as input by the user using a var_pop_up.