Visual LISP, AutoLISP and General Customization
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Error Trapping

9 REPLIES 9
SOLVED
Reply
Message 1 of 10
vivifira
2282 Views, 9 Replies

Error Trapping

Hi all,

 

So the last phase of my routine is error trapping. I've worked out the bugs and the last thing that would be great to have is a "catch all" error trap that will reset the usc, current layer and snaps to preset default values when the program exits unexpectantly. I've done a lot of digging and I haven't found anything that works. Please help. Thanks in advance. 🙂

9 REPLIES 9
Message 2 of 10
p_mcknight
in reply to: vivifira

This is my standard error trapper that I start with.  You can add settings to it or remove parts as needed by the specific routine.

 

The first line gets placed in with your local variable callouts

(/ *error* errMarker errTempSelectionSet currentOrthoMode currentOSMode

 

This section sets up the initial values prior to running anything in the actual code.

  (setq errMarker (entlast))

  (setq errTempSelectionSet (ssadd))

  (setq currentOrthoMode (getvar "orthomode"))

  (setq currentOSMode (getvar "osmode"))

  (setvar "cmdecho" 0)

  (command ".undo" "begin")

 

This is the actual error function that I set up within my function

  (setq originalError *error*)

  (defun *error* ( msg )

    (setq *error* originalError)

    (setvar "cmdecho" 0)

    (while (setq errMarker (entnext errMarker))

      (setq errTempSelectionSet (ssadd errMarker errTempSelectionSet ))

      )

    (command ".erase" errTempSelectionSet "")

    (setvar "orthomode" currentOrthoMode)

    (setvar "osmode" currentOSMode)

    (command ".undo" "end")

    (setvar "cmdecho" 1)

    (setvar "filedia" 1)

    (or (wcmatch (strcase msg) "*BREAK,*CANCEL*,*EXIT*")

(princ (strcat "\n** Error: " msg " **")))

    (princ)

    )

 

Don't forget to reset the error routine at the end of your function.  Hope this helps to get you started.

Message 3 of 10
vivifira
in reply to: p_mcknight

Again thanks for your help. I will try this and let you know the results. 🙂

Message 4 of 10
dgorsman
in reply to: vivifira

"Reset" would imply changing settings to values which have already been stored.  So, you need two parts: the first, to store settings when you feel there could be a problem; the second, to recall those settings when an error occurs.  The second is a little more complex, and involves creating an *error* function.

----------------------------------
If you are going to fly by the seat of your pants, expect friction burns.
"I don't know" is the beginning of knowledge, not the end.


Message 5 of 10
vivifira
in reply to: p_mcknight

BEAUTIFUL!!!!!!!!! This is exactly what I was looking for. Thanks for putting the final spit shine on my code.
Message 6 of 10
Kent1Cooper
in reply to: vivifira


@vivifira wrote:

....would be great to have is a "catch all" error trap that will reset the usc, current layer and snaps to preset default values when the program exits unexpectantly. I've done a lot of digging and I haven't found anything that works. Please help. Thanks in advance. 🙂


A few things:

 

There's no longer any need [since Release 14 or so, I think?] to save the current [presumably AutoCAD's native] error-handler, define your own, then after the command is done, reset the initial error handler to what it was.  You can now define *error* as a sub-routine, and if you include it in the localized variables list, it will apply only within the routine [as do all localized variables], and AutoCAD's own will come back into effect when it's done, without any resetting on your part.

 

Something I've taken to doing lately is to handle System Variable settings as a list, especially if there are more than one or two I need to change & reset.  You can make a list of their names, and collectively get all their initial values, and collectively set all the values you want them at, then at the end of the routine and in the error handler, collectively reset them all.  That way, you don't need a variable for each, and if you find in working out the routine that there's an additional one you need to deal with, you can simply add its name to the list, in one place, and not need to add the setting of another variable or the resetting of it as separate operations.

 

So, for example:

 

(defun C:YourCommandName
  (/ *error* svnames svvals var1 var2 var3)

 

  (defun *error* (errmsg)
    (if (not (wcmatch errmsg "Function cancelled,quit / exit abort,console break"))
      (princ (strcat "\nError: " errmsg))
    ); if
    (mapcar 'setvar svnames svvals)
    (princ)
  ); defun - *error*

 

  (setq
    svnames '(cmdecho clayer osmode blipmode); = System Variable NAMES
    svvals (mapcar 'getvar svnames); = System Variable VALueS
  ); setq
  (mapcar 'setvar svnames '(0 "LayerForYourRoutine" 0 0))

 

And then, to reset all those System Variables at once to their initial values at the end [as well as in *error* above]:

 

  (mapcar 'setvar svnames svvals)

 

However, in the case of the UCS, that's not a System Variable, so it needs to be handled differently.  Many of my routines may or may not change the UCS, in which case here's what I've often done.  If it might be changed only once, I'll include something like this where that would happen:

 

  (command "_.ucs" "_new" "_object" EntityName) ; set UCS to match object
  (setq ucschanged T) ; marker for *error* to reset UCS if routine doesn't get to it

 

Then when it's done with the changed UCS:

  (command "_.ucs" "_prev")

  (setq ucschanged nil); it has reset it, so it shouldn't later

 

At the end, and in *error*, I include:

 

  (if ucschanged (command "_.ucs" "_prev"))
    ;  ^ i.e. don't go back unless routine reached UCS change but didn't change it back

 

If one running of the routine might change the UCS more than once, UCS Previous won't be enough, so I save the current one at the beginning:

  (command "_.ucs" "_save" "UCSStemp")

and restore and then delete it at the end and in the error handler:

  (command "_.ucs" "_restore" "UCStemp" "_.ucs" "_del" "UCStemp")

Kent Cooper, AIA
Message 7 of 10
dgorsman
in reply to: Kent1Cooper

Doing it as a list makes it much easier, as it can be passed into a standardized function.  I've gone with a global error handler though, since most of my error handling is the same regardless of which function is failing.  In the few cases where it does have to be different, I have the local error handler call the global error handler after it does its own thing for consistency in handling returning settings.

----------------------------------
If you are going to fly by the seat of your pants, expect friction burns.
"I don't know" is the beginning of knowledge, not the end.


Message 8 of 10
vivifira
in reply to: dgorsman

Thanks again Kent. This is catching more of the errors my program has than what I first found. 🙂
Message 9 of 10
_gile
in reply to: vivifira

Hi,

 

To set, save and restore system variables, have a look at the ai_sysvar function defined in acad20XXdoc.lsp (it's a quite pretty use of defun-q).

 

(ai_sysvar  '("cmdecho" . 0)) ;; to save the current value and set cmdecho to 0

(ai_sysvar '(("orthomode" . 1) ("autosnap" . nil))) ;; to save the current values of orthomode and autosnap and set orthomode to 1

(ai_sysvar nil) ;; to restore all system variables



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 10 of 10
ВeekeeCZ
in reply to: Kent1Cooper

I am about to fix my long-time-overlooked issues with my *error* handlers... so picking up this nice Kent's explanation... 

 

Two situations:

 

1st - I have a long list of related routines (lets call that 'A' group) stored in one lsp file (really old ones and poorly coded) and these are sharing one global *error* trap. And since this package is always loaded... all the small routines with no its own error handler shares this global *error* as well. 

 

So I came up with this fix... and I'm interested if someone come with some better solution..

 

(defun c:TestA1 ( / :LoadErrorA *error*)
  (:LoadErrorA)
  (entsel)
  )

(defun c:TestA2 ( / :LoadErrorA *error*)
  (:LoadErrorA)
  (entsel)
  )

(defun :LoadErrorA ()
  (defun *error* (msg)
    (princ "A group *error*: ")
    (princ msg)
  )
)

2nd - guess this would be more common issue... How to handle *error* trap in more structural code with some sub-routines with its own *error* handlers...

 

Just figured out this approach... but you know, don't what to invent already invented.

 

(defun c:TestMain ( / *error* main_*error* :Sub)

  (defun *error* (msg)
    (princ "Main *error*: ")
    (princ msg)
    )

  (defun :Sub ( / *error*)

    (setq main_*error* *error*)
    (defun *error* (msg)
      (princ "Subroutine stuff returned.")
      (main_*error* msg))
    
    (entsel)
    )

  ; -----------

  (entsel)
  (:Sub)
  (princ)
)

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report

”Boost