Thursday, February 18, 2010

Winter Sim Presentation

This past December, I was fortunate enough to be able to present some of the work I've been doing with extracting data from Value Stream Maps and turning it into simulation data. I started off using QUEST as the simulation tool for the data, and last March (2009) I got the green light to start working on implementing the Core Manufacturing Simulation Data (CMSD) format. The CMSD is meant to be a generic simulation data interchange format, sort of like STEP is for CAD.

So, here's the presentation I made at Winter Sim. If you want to talk about it, you can contact me here.

Wednesday, January 20, 2010

Labor shirt colors

This is a stupid, however somewhat useful SCL macro. Sometimes I end up with a model with lots of laborers who all have the same color shirt. Call me crazy, but I think the model presents a little better if the labors don't all look the same.

So here it is, a stupid SCL macro you can use to change all your labors' shirt colors (note it won't let the color be the same as his pants or skin color (at least with the default laborer I'm using))

So here it is


SCL Examples

I recently received an email from a student using QUEST who was looking for examples of SCL.

I told him to first look at the default QUEST logics at QUESTlib\Sysdef\Logics, to see how QUEST uses SCL.

But I can't think of any good examples of how to write SCL macros. So I found an old macro I wrote that dumps all the process assignments for each laborer in a model to a tab-delimited text file. I figured it would be a good example of how to use SCL to get information out of a model, and shows some examples of different SCL syntax.

You can download it here, hopefully; I put it on Google Docs as just a file, so you should be able to go download it there.

Tuesday, September 1, 2009

Examples of using C_EXEC in SCL

SCL provides a function called C_EXEC which will allow you to execute functions in Windows DLL files. If you have used the Declare Function ... functionality in VBA then you'll see this is the same kind of functionality.

Basically, to use C_EXEC you need to know the name of the function in the DLL file, as well as its argument list. For example, the Sleep function resides in the kernel32.dll file that comes with Windows. It takes one argument, which is the sleep time in milliseconds. This function simply delays for the specified period of time, then returns control of the program back to whatever called it. This allows you to put a pause of whatever length you want in your program.

Since there is no built-in sleep function in SCL, this is a good function to have handy, always compiled and ready to use.

The syntax for the C_EXEC function is simple:

C_EXEC( routine_name_and_location , arg1 , arg...)

where routine_name_and_location is the dll filename and function name separated by a colon (:). Any extra arguments to C_EXEC should correspond to arguments in the DLL function.

So for the Sleep function, a C_EXEC call would look like this:
C_EXEC( 'kernel32.dll:Sleep' , 1000 )

Calling this in SCL would make your SCL code delay for 1 second.

We can wrap this in a nice routine that you can leave compiled all the time:

routine sleep( milliseconds : Integer ) : Integer
Const
THE_DLL 'kernel32.dll'
THE_CMD 'Sleep'
Var
func_return : Integer
Begin
/*
Public Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
*/
func_return = c_exec( THE_DLL + ":" + THE_CMD , \
milliseconds )
return true
End


Here's another example using the mciSendStringA function in winmm.dll to open your CD drive door (I know, very useful):

procedure open_cd_drive()
Const
THE_DLL 'winmm.dll'
THE_CMD 'mciSendStringA'
Var
func_return : Integer
lpstrCommand : String
lpstrReturnString : String
uReturnLength : Integer
hwndCallback : Integer
Begin

/*
Public Declare Function SendCDcmd Lib "winmm.dll" \
Alias "mciSendStringA" ( \
ByVal lpstrCommand As String, \
ByVal lpstrReturnString As String, \
ByVal uReturnLength As Long, \
ByVal hwndCallback As Long) As Long
*/
lpstrCommand = 'set CDAudio door open'
lpstrReturnString = ''
uReturnLength = 127
hwndCallback = 0

func_return = c_exec( THE_DLL + ":" + THE_CMD , \
lpstrCommand , \
lpstrReturnString , \
uReturnLength , \
hwndCallback )

End

Hopefully this gives you a good taste of what is possible with using C_EXEC to extend some of the functionality of SCL beyond what's currently available. If you want to see how you can create your own Windows DLL (and thereby provide your own custom functions in a compiled form) you can look at this web site. At some point in the future I hope to release a DLL library of functions to help add some functionality to SCL.

Monday, August 31, 2009

Converting SketchUp models to QUEST pdb files

SketchUp is a 3D modeling tool which was originally developed by @Last Software, which was purchased by Google. Google now provides the SketchUp tool for free, to encourage modelers to create real-life geometry for its Google Earth tool, as well as populating their 3D Warehouse, which is a huge repository of 3D models.

SketchUp is very easy to use, easy to learn, and very powerful for being a free tool. It's much easier to use than the QUEST CAD world, though the free version of SketchUp has some limitations which make it difficult to get SketchUp models into a format that QUEST will recognize. The free version will only export to the Collada format, which is not supported by QUEST. This means you have to use some intermediary program that can read Collada files and export to something QUEST understands (i.e. VRML 1.0, DXF, OpenFlight Obj).

Until now, the easiest way to get from SketchUp into QUEST was to shell out $495 for a license of SketchUp Pro, which includes a DXF exporter.

I say until now, because luckily the Guitar-List.com website has put together a Ruby plugin for SketchUp that exports from SketchUp to DXF, for free.

That's only half the battle, so with permission from Guitar-List.com I was able to modify their script to use QUEST's built-in DXF to PDB conversion tool so you can automatically export from QUEST to DXF to PDB.

You can download the ruby script here. Download it to your Program Files/Google/Google Sketchup/Plugins folder.

One pre-requisite for this script to work, however, is that you must have your dwg2pdb.bat file configured so that DENEB_PATH is set to whatever your Deneb or Delmia path is. You also must set the DENEB_PROD_DIR to point to the quest folder (default was vmap for me) and make sure LM_LICENSE_FILE is pointing to the right place. These settings should more or less mimic some of the settings from your quest.bat file.

Next copy the dwg_cmd file from your quest folder to the bin folder, and set your default units to inches.

The last thing you'll have to do is edit line 64 of the skp_to_pdb.rb file to point to your dwg2pdb.bat and dwg_cmd files.

It may take a bit of work to get set up, but I've been using it for a few weeks and it's nice to be able to use a modern 3D drawing tool and easily load the geometry in QUEST.

A few notes on usage, make sure you draw to the proper scale in SketchUp, and be mindful of the origin. If your geometry is far from the origin in SketchUp it'll be far from the origin in QUEST, too.

Again if you want to contact me directly just hit me up here or leave a comment.

Tuesday, August 25, 2009

How to run QUEST from Excel VBA and wait for QUEST to finish

This post is not meant to show you how you can use VBA to run BCL commands in QUEST. It's just a small tweak to how we run QUEST in this previous post. In that previous VBA solution, I used the baked-in Shell command to run a QUEST.bat file in BCL mode.

One reason I can think of for using this functionality is being able to automatically run some experimentation in QUEST, without having to mess with sockets (until we can get an easy to use library for QUEST sockets). This way we can have some Excel worksheet with accompanying VBA code that will run QUEST, wait for QUEST to exit, then check some output file from QUEST before advancing in the experiment. I am working on a project where this is pretty much exactly what I'll be doing.

To make this work, we just need to use a different Shell command, that will effectively halt execution of our VBA code while QUEST runs, and resume execution once the QUEST process we started ends.

Basically, we will re-use the code found here to replace our basic Shell call. All the linked code does is start QUEST using the Shell command, and uses a few Windows API calls to monitor the QUEST.exe process we started, looping until that process ends. Once the process ends, we exit the loop and VBA continues executing code.

I've updated the QUEST_BCL_Addin.xla file which can be downloaded here to include this new ShellAndWait functionality. If you call the SaveABCL function with LaunchQUEST = True and TransferToMenu = False then your code will wait for QUEST to exit before resuming.

I'd like to start adding some QUEST BCL User Defined Functions to this addin in the near future. I have some written already, but would like to get them properly formatted for release. If you have any you'd like to donate drop me a line using this contact form.

Split function in SCL

VBA has a function called Split that is handy for parsing strings to an array based on some delimiter.  All you have to do is dim out an array variable and say:
ArrayVar = Split( "the,split,string" , "," ) and your ArrayVar will contain three strings, "the" , "split", and "string".
There is no in-built function in SCL to do this, so I have put together something that should work in a similar fashion.  I have named the function the same, though the difference here is because SCL's arrays are fixed in size, where with VBA you can dynamically size arrays.
To get around this, you must first declare a String array in the Var section of your calling procedure (or globally), and pass that into the Split function as the last argument.  You must also provide the upper bound of the array to the function.  This upper bound is not the highest index of the array (which is one minus the size of the array), but just the size of the array.  One easy way to use this information is to declare your arrays using Const's denoting the size of the array.  For example:
procedure some_proc()
Const
arr_size 10
Var
my_array : Array[ arr_size ] of String
........
So, in this case, if you wanted to split a string into my_array, you would do this:
num_elems = split( 'some delimited line' , ' ' , arr_size , my_array )
where num_elems is the number of chunks of text split into the array.  If there are more chunks than there are bins in your array, any trailing chunks will be ignored, and no error will be raised.
I have tested this routine a bit, so it should work fairly well, even with multi-character delimiters.  On a side note, you should put this routine into your always available routines in QUEST.

routine split( the_line : String ; the_delim : String ; max_elements : Integer ; Var the_array : Array[] of String ) : Integer
Var
write_idx , delim_idx : Integer
check_char : String
Begin
while( len( the_line ) > 0 ) do
if( write_idx < max_elements ) then
delim_idx = index( the_line , the_delim , 1 )
--write( 'delim_idx=' , delim_idx , cr )
if( delim_idx > 0 ) then
the_array[ write_idx ] = leftstr( the_line , delim_idx - 1 )
the_line = rightstr( the_line , len( the_line ) - delim_idx - len( the_delim ) + 1 )
else
-- no more delimeters left - write the last of the_line to the array
the_array[ write_idx ] = the_line
the_line
= '' -- empty the_line so we break from the while loop
endif
write_idx = write_idx + 1
else
the_line = '' -- empty the_line so we break from the while loop
endif
endwhile
return write_idx -- return number of pieces written to array
End