error code fails

error code fails

Draftsman13
Contributor Contributor
1,937 Views
17 Replies
Message 1 of 18

error code fails

Draftsman13
Contributor
Contributor

We are using a routine that works well, if you run the routine from start to finish.  If you have to hit the "escape" button to exit out of the routine, it will crash on you.  The layers will not revert and for some reason, the osnap setting will reset to "0" layer.  From what I have read, the error code I have written should reset all local variables to the start of the program.  Could someone look at it and tell me how to fix it?  I already know I need to stop using a donut command and use a block instead, but have not had the time to update.  

 

;; This program installs a strut brace
;;; on the proper layers. If no layer
;;; exists, this program will create them.
;;;
;;; Program written by Kenneth Underhill
;;; Completed on December 17, 2008
;;; Revised April 12, 2018
;;;-------------------------------------------Program------------------------------------------------------------------------

(defun c:bc()

(setq temperr *error*) ; saves *error*
(setq *error* bcerr) ; sets the new error variable
(command "_.undo" "_mark") ; Starts the *undo* command

(graphscr)
(setq oldos (getvar "osmode")) ; Saves the current osnap setting
(setvar "osmode" 545) ; Sets the new osnap setting
(setq oldor (getvar "orthomode")) ; Saves the current ortho setting
(setvar "orthomode" 0) ; Turns off the ortho
(setq oldlayer (getvar "clayer")) ; Saves the current layer
(setq oldsc (getvar "selectioncycling"))
(setvar "selectioncycling" 0)
(command "_.layer" "_make" "STRUT" "_color" "1" "" "_ltype"
"continuous" "" "") ; Changes layer to Strut

(setq pt1(getpoint "\nSelect Brace Start Point: ")) ; Select brace starting point
(command "_.donut" "7" "9" pt1 "") ; Draws brace

(command "_.layer" "_make" "STRUT_LEG" "_color" "120" "" "_ltype"
"dashed2" "" "") ; Changes layer to Strut leg
(setvar "osmode" 0) ; turns osmode off

(setq pt2(getpoint "\nSelect Brace End Point: ")) ; Select brace end point
(setq don(entget(entlast))) ; selects the brace
(setq do(cdr(assoc 10 don))) ; finds the center of the brace
(command "_.line" pt2 "tan" do "" "_.mirror" "_last" "" pt2 "per" do "") ; Draws brace line to brace tangent and then mirrors it

(setvar "osmode" oldos) ; Sets the osnap to original settings
(setvar "orthomode" oldor) ; Sets the Ortho to original settings
(setvar "clayer" oldlayer) ; Sets the layer to original layer
(setq *error* bcerr) ; restores original error
(command "_.undo" "end") ; Ends the *undo* command

(princ)
) ; End Brace

;;;----------------------------ERROR-----------------------------------------------------------------------------

(defun bcerr (errmsg) ; defines error

(command "_.undo" "_back") ; removes any work done under this command
(setvar "osmode" oldos) ; sets the osnaps to previous settings
(setvar "orthomode" oldor) ; restores the ortho to previous setting
(setvar "clayer" oldlayer) ; sets the current layer to previous setting
(setvar "selectioncycling" oldsc)
(setq *error* bcerr) ; restores original error
(prompt "\nResetting System Variables ") ; prompts user

(princ)
) ; end error

 

0 Likes
Accepted solutions (1)
1,938 Views
17 Replies
Replies (17)
Message 2 of 18

ronjonp
Mentor
Mentor

Is it supposed to draw something like this? .. if so all I did was localize your variables and it worked.

(defun c:bc (/ *error* bcerr do don oldlayer oldor oldos oldsc pt1 pt2 temperr)


image.png

0 Likes
Message 3 of 18

Draftsman13
Contributor
Contributor

yes, it is supposed to look like that.  The problem is after you select the first point, but before selecting the second point.  If you hit the escape button the layer will stay on "strut_leg" instead of reverting to the current layer and the osnap setting will be changed to "0" instead of the current setting.  Not a big deal for me, but frustrating and time consuming for some of my weaker Acad users.

0 Likes
Message 4 of 18

Draftsman13
Contributor
Contributor

I should mention I tried your solution and am still having the same issue.

 

I should also mention we are using AutoCAD 2016 full version

0 Likes
Message 5 of 18

ronjonp
Mentor
Mentor

Try this:

;; This program installs a strut brace
;;; on the proper layers. If no layer
;;; exists, this program will create them.
;;;
;;; Program written by Kenneth Underhill
;;; Completed on December 17, 2008
;;; Revised April 12, 2018
;;;-------------------------------------------Program------------------------------------------------------------------------

(defun c:bc (/ *error* do don pt1 pt2 temperr v x) ; <- localize variables 
  (defun *error* (msg)
    ;; RJP » 2018-08-17
    ;; List of saved vars to restore on error
    (mapcar '(lambda (x) (setvar (car x) (cdr x))) v)
    (or (wcmatch (strcase msg) "*BREAK,*CANCEL*,*EXIT*") (princ (strcat "\n** Error: " msg " **")))
    (princ)
  )
  (command "_.undo" "_mark")		; Starts the *undo* command
  (graphscr)
  ;; Consolidate variables into list
  (setq v (mapcar '(lambda (x) (cons x (getvar x))) '(osmode orthomode clayer selectioncycling)))
  (setvar "osmode" 545)			; Sets the new osnap setting
  (setvar "orthomode" 0)		; Turns off the ortho	; Saves the current layer
  (setvar "selectioncycling" 0)
  ;; Check if we actually have 2 picked points
  (if (and (setq pt1 (getpoint "\nSelect Brace Start Point: "))
	   (setq pt2 (getpoint pt1 "\nSelect Brace End Point: "))
      )
    (progn (command "_.layer" "_make" "STRUT" "_color" "1" "" "_ltype" "continuous" "" "")
					; Changes layer to Strut
	   (command "_.donut" "7" "9" pt1 "") ; Draws brace
	   (command "_.layer" "_make" "STRUT_LEG" "_color" "120" "" "_ltype" "dashed2" "" "")
					; Changes layer to Strut leg
	   (setvar "osmode" 0)		; turns osmode off
	   (setq don (entget (entlast))) ; selects the brace
	   (setq do (cdr (assoc 10 don))) ; finds the center of the brace
	   (command "_.line" pt2 "tan" do "" "_.mirror" "_last" "" pt2 "per" do "")
					; Draws brace line to brace tangent and then mirrors it
	   (command "_.undo" "end")	; Ends the *undo* command
    )
  )
  ;; Reset the variables
  (mapcar '(lambda (x) (setvar (car x) (cdr x))) v)
  (princ)
)
0 Likes
Message 6 of 18

Kent1Cooper
Consultant
Consultant
Accepted solution

Or, with less of a wholesale re-write, these may not fix everything, but they caught my eye:

 

Change this inside bcerr:

(command "_.undo" "_back")

to this:

(command-s "_.undo" "_back")

In Acad2016, you should be getting an error message about using (command) in the *error* handler, suggesting this change.  That's probably cancelling the rest of *error*, so those other things don't get reset.

 

Change this:

(setq *error* bcerr) ; restores original error ; bcerr isn't the original!

to this:

(setq *error* temperr) ; restores original error

Kent Cooper, AIA
0 Likes
Message 7 of 18

Draftsman13
Contributor
Contributor

This does solve the error issue.  However:

1. I do not understand what is written.  My coding ability is really just glorified macros.

2. It removes some of the functionality of the original command.  Sometimes we just want the circle and not the tail.  This re-write does not allow for that. 

 

Thank you for the responses, tho.

0 Likes
Message 8 of 18

Draftsman13
Contributor
Contributor

This appears to solve the issue.  I cannot tell you how many times I have looked this over.  One question: if using the command function, do they all need to be written as "command-s", or just this one?

0 Likes
Message 9 of 18

ronjonp
Mentor
Mentor

@Draftsman13 wrote:

This does solve the error issue.  However:

1. I do not understand what is written.  My coding ability is really just glorified macros.

2. It removes some of the functionality of the original command.  Sometimes we just want the circle and not the tail.  This re-write does not allow for that. 

 

Thank you for the responses, tho.


Keep practicing and you'll get there 🙂 .. you really should check that the points are picked though. Here's a simplified version of that logic:

(defun c:bc (/ *error* do don pt1 pt2 temperr v x) ; <- localize variables 
  (defun *error* (msg)
    ;; RJP » 2018-08-17
    ;; List of saved vars to restore on error
    (mapcar '(lambda (x) (setvar (car x) (cdr x))) v)
    (or (wcmatch (strcase msg) "*BREAK,*CANCEL*,*EXIT*") (princ (strcat "\n** Error: " msg " **")))
    (princ)
  )
  ;; Consolidate variables into list
  (setq v (mapcar '(lambda (x) (cons x (getvar x))) '(osmode orthomode clayer selectioncycling)))
  (setvar "osmode" 545)			; Sets the new osnap setting
  (setvar "orthomode" 0)		; Turns off the ortho	; Saves the current layer
  (setvar "selectioncycling" 0)
  (command "_.undo" "_mark")
  ;; point was picked for donut so change layer and create
  (if (setq pt1 (getpoint "\nSelect Brace Start Point: "))
    (progn (command "_.layer" "_make" "STRUT" "_color" "1" "" "_ltype" "continuous" "" "")
	   (command "_.donut" "7" "9" pt1 "")
    )
  )
  ;; Donut was created so prompt for brace or enter to do nothing
  (if (and pt1 (setq pt2 (getpoint pt1 "\nSelect Brace End Point: ")))
    (progn (command "_.layer" "_make" "STRUT_LEG" "_color" "120" "" "_ltype" "dashed2" "" "")
	   (setvar "osmode" 0)		; turns osmode off
	   (setq don (entget (entlast))) ; selects the brace
	   (setq do (cdr (assoc 10 don))) ; finds the center of the brace
	   (command "_.line" pt2 "tan" do "" "_.mirror" "_last" "" pt2 "per" do "")
    )
  )					; Draws brace line to brace tangent and then mirrors it
  (command "_.undo" "end")		; Ends the *undo* command
  ;; Reset the variables
  (mapcar '(lambda (x) (setvar (car x) (cdr x))) v)
  (princ)
)
0 Likes
Message 10 of 18

Kent1Cooper
Consultant
Consultant

A few other quickie comments:

 

You don't ever need to assign Continuous as the linetype for a new Layer.  It's the default.

 

Ever since [I think] Release 14 [no, I don't mean AutoCAD 2014 -- talking quite a while ago] it's been allowable to localize  the *error* function [as @ronjonp's code does] like a variable, which means the definition of it inside the command definition goes away on its own when the command is over, so you don't  need to save the current version to a variable and restore it later.

 

I would not set OSMODE to 545 -- that includes NEArest mode [with ENDpoint and INTersection].  Except in limited circumstances, NEA will always "win out" over END and INT, because there will be some point on the object that it sees that's closer to the cursor location than the END or INT location.  So you will only sometimes  actually get your pt1 at  an ENDpoint or INTersection when that's your intention.

 

(cdr (assoc 10 don)) doesn't get you the center of the Donut.  It would if that were a Circle, but a Donut is a Polyline, and that gets you its first vertex [which will be at the left QUAdrant point].  That works in this case, because of the "tan" Osnap and the fact that Mirroring means it doesn't matter which side is drawn first.  But I would at least change the comment part of that line of code.

Kent Cooper, AIA
0 Likes
Message 11 of 18

Kent1Cooper
Consultant
Consultant

@Draftsman13 wrote:

... if using the command function, do they all need to be written as "command-s", or just this one?


Only the one inside the *error* handler needs to be [and you can even get around that, but it takes some other shenanigans].  There can be reasons why you might want to use that instead, in other circumstances -- read about it in Help.

Kent Cooper, AIA
0 Likes
Message 12 of 18

scot-65
Advisor
Advisor
You have just witnessed proper programming structure.

To recap:
a) Get user input [use default environment settings including OSMODE].
b) Test user input [a simple IF (with AND, as required) will do the trick].
c) Set environment, Execute and Reset environment [no user interaction allowed,
not even a pause inside a command].

By writing your utilities following this structure no error handler will be required!

Simple example:
(if (and (setq p1 (getpoint "Specify first point: "))
(setq p2 (getpoint p1 "Specify end point: ")) );and
(progn
(setq o_osm (getvar 'OSMODE))
(setvar 'OSMODE 0)
(command-s ".LINE" P1 P2 "")
(setvar 'OSMODE o_osm)
);progn
);if

Now, ask yourself:
"What would happen if one presses [Esc] for either one of the getpoints shown above?"
If you answered "nothing", then no error handler is required.

COMMAND-S does not accept "pause" as well as "chaining" commands together.

???

Scot-65
A gift of extraordinary Common Sense does not require an Acronym Suffix to be added to my given name.

0 Likes
Message 13 of 18

Draftsman13
Contributor
Contributor

@Kent1Cooper wrote:

 

I would not set OSMODE to 545 -- that includes NEArest mode [with ENDpoint and INTersection].  Except in limited circumstances, NEA will always "win out" over END and INT, because there will be some point on the object that it sees that's closer to the cursor location than the END or INT location.  So you will only sometimes  actually get your pt1 at  an ENDpoint or INTersection when that's your intention.

 

Because of the way this routine is used, we actually need the NEAest to take precedent and sometimes get ENDpoint or INTersection.

 


@Kent1Cooper wrote:

 

(cdr (assoc 10 don)) doesn't get you the center of the Donut.  It would if that were a Circle, but a Donut is a Polyline, and that gets you its first vertex [which will be at the left QUAdrant point].  That works in this case, because of the "tan" Osnap and the fact that Mirroring means it doesn't matter which side is drawn first.  But I would at least change the comment part of that line of code.

 

I have seen a similar code to this one that uses a block instead of a donut, although the block is still a polyline.  Would the (cdr (assoc 10 don)) register the center?  My intention is to have a circle with thickness so it shows better in plots.  Is there a better way to do this?  Or should I leave well enough alone since it works as is?


 

0 Likes
Message 14 of 18

Kent1Cooper
Consultant
Consultant

@Draftsman13 wrote:

.... 

Because of the way this routine is used, we actually need the NEAest to take precedent and sometimes get ENDpoint or INTersection.


....

I have seen a similar code to this one that uses a block instead of a donut, although the block is still a polyline.  Would the (cdr (assoc 10 don)) register the center?  My intention is to have a circle with thickness so it shows better in plots.  Is there a better way to do this?  ....


 

I would just use NEArest by itself.  It will serve alone in any situation I can think of in which you would get the same result as from ENDpoint or INTersection.

 

The 10-code in that situation will get you the insertion point of the Block, wherever that may be.  That's fine if you can be assured that the Block will always be defined with its insertion point at the center.  I would consider making them Circles, but in a color that you set up to print at the kind of lineweight you want.

Kent Cooper, AIA
0 Likes
Message 15 of 18

Draftsman13
Contributor
Contributor

Ok, so after looking at this for a while, I still have issues.  I have changed the command to command-s and the bcerr to temperr as recommended:

(setq temperr *error*) ; saves *error*
(setq *error* bcerr) ; sets the new error variable
(command-s "_.undo" "_mark") ; Starts the *undo* command

(defun bcerr (errmsg) ; defines error

(command-s "_.undo" "_back") ; removes any work done under this command
(setvar "osmode" oldos) ; sets the osnaps to previous settings
(setvar "orthomode" oldor) ; restores the ortho to previous setting
(setvar "clayer" oldlayer) ; sets the current layer to previous setting
(setvar "selectioncycling" oldsc)
(setq *error* temperr) ; restores original error
(prompt "\nResetting System Variables ") ; prompts user

 

Whenever I use the command and hit escape in the middle, i get:

"Select Brace End Point: *Cancel*
; error: Function cancelled"

The layer and the osnap does not revert to before the command was started.  I am not sure what I am doing wrong.  I need an error that will let me hit escape and exit cleanly and keep all my settings.  I need to correct this issue on several routines I have written.  Several people use these and we do not always notice immediately when we need to change our settings back.  
  

0 Likes
Message 16 of 18

Kent1Cooper
Consultant
Consultant

@Draftsman13 wrote:

....  I have changed the command to command-s and the bcerr to temperr as recommended....


Post the entirety of the definition of 'bcerr' [what you posted doesn't get to the completion of it].  And consider the first more-than-one-line paragraph in Message 10, to avoid the need for 'bcerr' entirely.

Kent Cooper, AIA
0 Likes
Message 17 of 18

Draftsman13
Contributor
Contributor

That may be the issue.  That is the entirety of the error code that I have.  So, I can get rid of the error coding I have if I just localize the current error for the routine?  I normally do not localize my variables because I cannot get the routines to work when I do.  That probably means I am not catching all of the variables in my routines, though.  As I said, I have several routines that use this error coding, so I am looking for a versatile solution.

0 Likes
Message 18 of 18

roland.r71
Collaborator
Collaborator

@Draftsman13 wrote:

That may be the issue.  That is the entirety of the error code that I have.  So, I can get rid of the error coding I have if I just localize the current error for the routine?  I normally do not localize my variables because I cannot get the routines to work when I do.  That probably means I am not catching all of the variables in my routines, though.  As I said, I have several routines that use this error coding, so I am looking for a versatile solution.


 

You should always localize your variables, unless a global is required. (& I don't get your "reason", its reverse)

There are a few (mayor) problems with leaving everything global.

1) it eats memory, even long after the routines finnished. Running more routines = more memory lost.

2) routines can actually mess up eachothers variable values.

3) variables can contain values where there should be non. (when running a routine a second time)

 

Whenever you MUST use a global (for remembering previous user choices (session wide) for example)

always try to use a name that is extremely unlikely to be used by other functions (ANY function, not just yours)

& add some asterix before and after, which is not required but just good practice to show the world THIS is a global.

Much like the original error (*error*). Something like *myFunc_myGlobalVar*

 

& as mentioned, catching the error function and restoring it later is kinda obsolete and has a mayor drawback itself.

Due to errors occuring I've seen it frequently happening the original does not get restored. Leading to all sorts of crappy situations with other functions invoking the error function, which can be hard to pinpoint. ('cause it looks as if THAT function has an error (you can't find) where it is caused by yours replacing the original error function.)

 

Just declare it local:

 

(defun myFunc ( / *error*) ; make the error local to this function (function ends, original error is available again)
   (defun *error* (msg / ) ; define the error function as required for THIS function
      ; do whatever needs to be done to exit nicely
   )
)

Note: Testing variables to contain what they should contain before using them will prevent 9/10 errors.

0 Likes