Monday, March 2, 2009

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.

Thursday, December 18, 2008

Compiling SCL from SCL (ish)

I don't know about you, but in the past I've developed QUEST solutions where it would have been nice to be able to use SCL code to identify a set of other SCL files to compile.  However, there is no straightforward way to do this as using the SCL_COMPILE BCL command from an SCL script will generate an error and not compile the SCL file.

There is a workaround, however, and that is to use BCL to execute an SCL subroutine which returns the path of an SCL file to the bcl_msg BCL variable.  You can then use the SCL_COMPILE command passing bcl_msg as an argument to compile an SCL file selected using SCL code.

The downside is that if the routine doesn't return a file path the BCL code still tries to compile the blank string file path, and shows an error in the message window.  Unfortunately (as far as I can tell) you can't use conditional statements in a BCL script, so there's not much we can do about this.  I'd say if you need to be able to compile SCL from SCL, it's worth the small inconvenience of seeing the error message when you don't specify a file.

I've used this technique to compile SCL files listed in a configuration file (not the QUEST kind) that could only be parsed using SCL (rather than straight BCL), so that users can specify different SCL logics in building a QUEST model from an eVSM file.

This may not be of any use to anyone reading this, so I've provided an example which hopefully will make using QUEST just a little bit easier to use.  I don't know about you, but I often debug my SCL code by writing a little, then compiling it in QUEST, until it's all done and working.  However, if you're working on multiple SCL files it can be a pain to go to the Run->Simulate->Compile button and pick files in various folders.

So what I've done is created a BCL macro that calls an SCL routine that keeps track of (up to) the last 49 or so SCL files that I've compiled (in the current session) and gives me the option of selecting another file to compile.  So I've basically duplicated the Run->Simulate->Compile functionality, and added a list of the most recently compiled files for you to chose from.

You can get the BCL script here and the corresponding SCL file here.  Keep in mind that you need to modify the BCL script to compile the SCL file wherever it is saved on your computer.

Tuesday, November 11, 2008

Select an Element

QUEST SCL provides a function for selecting items (including elements) from the QUEST world, aptly named Select.   Basically, you tell the function what you want to pick (elements, cad_parts, etc...), the selection mode (mouse pick, pick from a list, or key in the name), and you pass in a string variable that will be set by the routine to the selected item's name (You also have to specify a text prompt, and whether or not you want the selected item to be highlighted).

I usually don't need to use all the available options in the Select function, so I've put together a routine I use often that I can drop into an SCL macro or call with an Extern reference, so here it is:

   routine select_an_element( prompt : String ) : Element
   Var
   elem_name : String
   picked_elem : Element
   Begin
      while( select( prompt , ELEMENT , PICK_MODE , TRUE , elem_name ) ) do
         picked_elem = get_element( elem_name )
      endwhile
      return picked_elem
   End

Like I said, this is just an easy to implement routine you can drop into your code without worrying too much about it, which is how I like to program...