Batch-processing AutoCAD drawings from LISP without SDI

As mentioned in this previous post, there has been some discussion internally around the future of SDI. Given the change this will bring to applications, SDI is going to be around until we next deliberately choose to break binary application compatibility (something we just did with AutoCAD 2010 and typically try to do only every three releases).

That said, SDI is very likely to go away at some point, so it does seem worth drilling further into the reasons for using it and trying to determine an appropriate way to remove current dependencies on it.

Thanks to all of you who responded to my previous post and provided input on your use of SDI. The most common theme was around the use of SDI to batch-process sets of drawings: opening each one, performing an operation and (optionally) saving before opening the next.

My understanding is that SDI makes life easier, in this situation, because the closing of one drawing is taken care of automatically when opening the next and so LISP applications can operate more easily across multiple drawings.

I did some tests, to see how it helps, but the LISP part of my brain has unfortunately atrophied, over the years: I wasn't able to get a simple SDI-only, batch processing application to work, as it always stopped once a new drawing was open. It may be that some use of script files is needed – and this certainly can make life easier, as we'll see below – but it would be good if someone could help me out by posting a comment or dropping me an email. I'm sure I'm missing something very simple.

Anyway, irrespective of whether I was able to use SDI successfully, or not, I was able to get something working from an MDI environment that I hope to be equivalent, functionality-wise.

Thanks to guidance from Wayne Brill, a member of DevTech Americas, I was able to put together some LISP code that makes use of a temporary script file to handle the opening, processing, (saving) and closing of drawings.

Here's the LISP application:

(defun C:BATCH(/ dwgs scr-name lsp-name)

  (setq dwgs '("C:/A.DWG" "C:/B.DWG" "C:/C.DWG" "C:/D.DWG")

        scr-name "c:/tmp.scr"

        lsp-name "c:/batch.lsp"

  )

  (create-script scr-name dwgs lsp-name "(CreateCircle)" T)

  (command "_.SCRIPT" scr-name)

  (vl-file-delete scr-name)

  (princ)

)

 

(defun CreateCircle()

  (command "_.CIRCLE" "0,0,0" "30")

)

 

(defun create-script(scr dwgs lsp cmd save / f dwg)

  (setq f (open scr "w"))

  (foreach dwg dwgs

    (progn

      (write-line

        (strcat "_.OPEN \"" dwg "\"") f

      )

      (write-line

        (strcat "(load \"" lsp "\")") f

      )

      (write-line cmd f)

      (if save

        (write-line "_.QSAVE" f)

      )

      (write-line "_.CLOSE" f)

    )

  )

  (close f)

  (princ)

)

The script handles the opening of each drawing, reloading the LISP file (which I have saved in c:/tmp.lsp – this file is pointed at by the lsp-name variable in the C:BATCH function) inside each one and running the specified command/function before saving & closing. In this case we're running a simple function that uses a command to create a circle – if we were doing something that didn't require the drawing to be saved (if we were just querying data, for instance) we could pass nil instead of T into the (create-script) function.

To take a look at the script being used behind the scenes, you can simply comment out the call to (vl-file-delete) and open up the contents in your favourite text editor:

_.OPEN "C:/A.DWG"

(load "c:/tmp.lsp")

(CreateCircle)

_.QSAVE

_.CLOSE

_.OPEN "C:/B.DWG"

(load "c:/tmp.lsp")

(CreateCircle)

_.QSAVE

_.CLOSE

_.OPEN "C:/C.DWG"

(load "c:/tmp.lsp")

(CreateCircle)

_.QSAVE

_.CLOSE

_.OPEN "C:/D.DWG"

(load "c:/tmp.lsp")

(CreateCircle)

_.QSAVE

_.CLOSE

I hope this approach goes some way towards helping people batch-process drawings from LISP without having to move to a different language. I'd really appreciate your feedback on this subject, as it would be good to get a more definitive approach nailed down before this change (eventually) becomes a requirement.

20 responses to “Batch-processing AutoCAD drawings from LISP without SDI”

  1. Kean,

    The problem that I had run into before is that if you inadvertently modify the drawing database with the script (say with PLOT)then when you close you get a "Do you want to save?" prompt that halts the script. Or even worse, if you have a line that zooms extents, but only does it on some of the drawings in the batch, then you have some of the drawings in the batch that will have a save prompt and some that won't. I'm over simplifying the issue here to save space, so I hope my explanation makes some sense.

  2. Kean Walmsley Avatar

    Jon,

    Right now the call to QSAVE should handle that, but yes: if you don't save then there is the chance DBMOD being non-zero could cause a problem.

    It should be simple enough to add something to the script to check DBMOD before closing, sending an additional "_Y" in the case of it being non-zero.

    Cheers,

    Kean

  3. Kean

    Re the use of SDI to batch-process sets of drawings, I've always found it safer to loop at the OS level.

    My batch/VBS file will open AutoCAD (passing the drawing and a script file on the command line) perform the required actions, save and exit AutoCAD, for each file to be processed.

    My experience is that as you go through the open/close cycle within a single session of AutoCAD, memory is not always released, and the RAM usage slowly creeps up, until AutoCAD finally gives up.

    And as my batch processes always seem to involve hundreds of files, I always go for reliability rather than speed.

    Nick

  4. Kean Walmsley Avatar

    Nick,

    Thanks - that's useful input. I can well imagine that certain cases need more fault-tolerance (especially when you're processing DWGs that may not load properly).

    What I'm looking at with this are the more simple cases. But even the approach in this post (using a single script) could be improved... the next version I'll post will just use a script to chain to the next file (to replace the use of (command "_.open" ...) when SDI == 0). This should help the cases where certain commands break script execution.

    Cheers,

    Kean

    P.S. I should probably have mentioned ScriptPro, for completeness, although I know that's not a solution for people on 64-bit Windows.

  5. The problem I have had before was plotting and updating Title blocks with lisp. I would have to set SDI = 1, and LispInit = 0. Then I would run my lisp that would do either mentioned above. Here is the main part ( you will not be able to run it, but can see what it is doing ).

    Can't figure out how to show the code indents.


    (progn
    (setq Oldsdi (getvar "sdi"))
    (setq Oldlsp (getvar "lispinit"))
    (setvar "sdi" 1)
    (setvar "lispinit" 0)
    (foreach Dwg DwgList
    (setq FullName (strcat DirPath Dwg))
    (if (/= (getvar "dbmod") 0)
    (command "_.open" SaveOpt FullName)
    (command "_.open" FullName)
    )
    (if (setq TitleBlock (GetTitleBlock (vla-get-ActiveDocument (vlax-get-Acad-Object))))
    (progn
    (setq AttLists (GetRevLevels TitleBlock))
    (ReOrderRevisions TitleBlock AttLists)
    (setq AttLists (GetRevLevels TitleBlock))
    (if (= NewRev "0")
    (UpdateLastRevision AttLists RevList)
    (AddRevision TitleBlock AttLists RevList)
    )
    (command "_.qsave")
    )
    )
    )
    (setvar "sdi" Oldsdi)
    (setvar "lispinit" Oldlsp)
    )

  6. > "...break binary application compatibility"

    What does that mean? I know what each one of those words mean but when they are used together like that...

    To get your code to format, wrap the code in HTML "< PRE >" and "< / PRE >" tags. Like this:

    ((lambda (x) 
    (list x (list (quote quote) x)))
    (quote
    (lambda (x)
    (list x (list (quote quote) x)))))
  7. AutoCAD loads a number of C++-compiled application modules (mainly DLLs, but some are renamed .ARX and .DBX). The C++-based interfaces these modules use remain "binary compatible" (which means they don't need to be recompiled from source code) for a three release window. Beyond that C++ developers are asked to rebuild - and potentially migrate some parts of their source code.

    Kean

  8. Ahh, got 'cha. Thanks.

  9. Randall Lamond Avatar

    Nick , would it be possible to get a copy of your batch open autocad script , I am having the same problem with ram creep.
    I understand if you wish not to pass it on

    Many thanks

  10. Hi Randall,

    Be sure to check our the ScriptPro 2.0 Plugin of the Month, if you haven't already.

    Regards,

    Kean

  11. Kean,

    Is there any updates for this for AutoCAD 2014 or anything else to use in the current version of AutoCAD. Trying to use it with AutoCAd 2013 and each file always say fail for the status even if the scripts completes correctly on the file.

  12. Bill,

    If using AutoCAD 2013+ then I'd suggest looking at using the Core Console in combination with ScriptPro 2.0 for this.

    Regards,

    Kean

  13. Kean,

    I have been working on a lisp only batch processor that works pretty well with one exception. I shell out to open the drawing list but in the process, if a drawing takes to long to process then the shell creates a new instance of Autocad and starts loading drawings there. This is not a huge issue but upon completion the user may have to close a few instances of Autocad. Do you know of a way to check for the current instance and open the drawings in just that instance? Here is the code I use to open the drawings.


    ;;; ------------ PROCESS THE SELECTED DRAWINGS
    (defun BATCH_PROCESS (FileList / Shell File)

    ;; Copy the batch to the temp folder - rename it with temp name
    (vl-file-copy (strcat BATCH_System BatchProcess)(setq BatchProcess (vl-filename-mktemp "BATCH" BATCH_User ".batch")))
    ;; Set the Batch config
    (vl-registry-write BatchMasterKey "Batch" BatchProcess)
    ;; Create the shell object - *USING THE SHELL OPENS THE DWG IN THE DEFAULT APP FOR DWG's
    (setq Shell (vla-getInterfaceObject (vlax-get-acad-object) "Shell.Application"))
    ;; Process each drawing
    (foreach File FileList
    (if (setq File (findfile File))
    ;; Open each drawing
    (vl-catch-all-apply (function vlax-invoke)(list Shell 'Open File))
    )
    )
    ;; Release the shell object
    (vlax-release-object Shell)
    )

  14. Tim,

    I'm surprised you've got this working as well as you appear to have, considering the fact LISP is so heavily document-centric. I don't know how you can get over the problem you're experiencing, although you may be able to access the ROT (Running Object Table) via COM and close instances that you don't care about.

    I have to ask though: is there a good reason you're not just using ScriptPro 2.0 for this?

    Regards,

    Kean

  15. Kean,

    The company I work for is not very interested in anything that isn't OTB. They are very set in there ways and do not like change, they don't want to rely on something that may not be there tomorrow.

    The batch processor works quite well. It adds a line of code in the acaddoc.lsp (if it isn't there already):

    ;; BATCHWORX
    ;; ******************************BATCHWORX START********************************
    ;; Applaod the bathworx program
    (load "Batchworx.lsp")
    ;; ******************************BATCHWORX END**********************************

    This will load the batch processor. In the batch processor we check for a few registry keys as well as a file in a folder (.batch) If the criteria is met it loads the batch. (these parameters are set upon running the batch processor. At that time a reactor is set which check for the drawing to take focus again and that the registry keys have been set. At that point it starts opening the list of selected drawing and because the parameters are set, it loads the .batch file. The contents of the .batch file are added the S::STARTUP so once the drawing is fully loaded the batch runs then closes the drawing. Once all of the drawing are processed and the first drawing takes focus, the reg keys are reset and the .batch file is deleted from the folder, and the reactor is turned off. One of the registry keys is a cleanup switch. If this is set (to a lisp, .batch, exe, script, can be anything) the cleanup function then runs. (this can be things like zipping a folder of drawings, emailing, what ever needs to be done. I have almost finished the documentation, once I do I will post to the TheSwamp, along with the batch processor.

    Other than the registry that store the switches, this is a lisp only batch processor.

  16. Tim,

    ScriptPro 2.0 is provided with full .NET source code, which should give some confidence that even if it "goes away" you can maintain it yourself (not true of the prior ScriptPro).

    But anyway - the choice is yours. You should be aware that doing this kind of thing from LISP comes with its own risks and complications (the API is not designed to do heavyweight batch processing at a document level), and as the move away from fibers continues inside AutoCAD there may even be some work to do to keep it working.

    Regards,

    Kean

  17. Kean,

    I fully understand that this sort of process takes special consideration, but I ask you this. What I am doing (through lisp) is no different than what I would do manually; Open a drawing, run some sort of process (either manually or programmatically), save, close, repeat. Why do you say the API isn't designed for it? Isn't that essentially what we do everyday?

    I used Script Pro long ago but found that if a script errors, the entire process comes to a halt. Has that changed? Is my work for not?

    Just trying to get a lot of input on this subject as it is one of those subjects that has been around forever, it is always good to look at it with a different viewpoint.

    Thanks

  18. Tim,

    LISP is fundamentally an API that excels in the document context. This is also largely true of ObjectARX and .NET, for that matter. COM is the one AutoCAD API that has been designed to work well across multiple documents.

    I suggest taking a look at ScriptPro 2. This version works really well with instances that halt for whatever reason: you can specify a timeout after which the instance will be killed if it hasn't completed successfully - the failure also gets reported to the user, of course.

    Regards,

    Kean

  19. Hi Kean, you could also take a look at Hurricane for AutoCAD. It does the script generation for you. It not only does the batch scripting, but also mass file-renaming, AutoCAD version checking and a dozen other things that save time. Hope you have time to check it out. 74mph.com

  20. I know this is a decade old tread but I would just like to say that..
    You used to be able to batch process a whole sheet set using acaddoc.lsp and startup while publishing a sheet set. You could even create a sheet set archive if you were uncertain if everything would process as you expect. And you could look at the pdf file and verify the results right afterwards without even opening the drawings. I used to do this all the time until Autodesk taketh away. Now we have threads like this and its all very very very sad. This used to just work. I have processed hundreds if not thousands of dwgs this way before it was taken away. Couldn't we just have another system variable to make this work again?

Leave a Reply to Bill Kishonti Cancel reply

Your email address will not be published. Required fields are marked *