The AutoLISP programming language supports some basic debugging techniques that can be utilized to help troubleshoot problems within a custom program. Displaying messages or values during the execution of a custom program can be beneficial in identifying the statement that is failing during execution. By looking at the last message or value returned, it will give you a basic or exact idea on where to start looking for the problem within the custom program based on the number of messages placed throughout your custom program.
These AutoLISP functions can be helpful in providing basic information in the AutoCAD user interface which can be used to determine where a program might have failed:
alert
– Displays a message box containing a text message.
Syntax:(alert message)
princ
– Displays a value assigned to a variable or returned by an expression, or a text message at the Command prompt. This function can also be used to write a value to an external file when the optional file descriptor is provided.
Syntax:(princ [expression [file_descriptor]])
prompt
– Displays a text message or string value at the Command prompt.
Syntax:(prompt message)
You might use the previously mentioned functions to display basic or more specific messages to help identify which code statements were recently executed. Examples of basic messages might be:
- "Here1", "Here2", "HereX"
- "Debug1", "Debug2", "Debug3"
- "A", "B", "C"
More specific messages that you might use are:
- "Starting While loop to modify selected objects"
- "Updating DXF data for an object"
- "Removing dictionary entries"
The previously mentioned functions are useful for displaying information in the AutoCAD user interface; it can also be beneficial to write messages and values out to an external log file. In addition to the PRINC
function, the following AutoLISP functions can also be used to output information to the Command prompt or an external log file:
prin1
– Displays a value assigned to a variable or returned by an expression, or a text message at the Command prompt or writes it out to an external file if a file descriptor is provided. Strings containing control characters are expanded.
Syntax:(prin1 [expression [file_descriptor]])
print
– Displays a value assigned to a variable or returned by an expression, or a text message at the Command prompt or writes it out to an external file if a file descriptor is provided.
Syntax:(print [expression [file_descriptor]])
The following code example creates a blue circle using the ActiveX/COM library and demonstrates how some of the previously mentioned functions can be used to help debug a program:
; Load COM environment
(vl-load-com)
; Create a blue circle using Visual LISP
(defun c:CreateCircle-VL ( / acadObj docObj spaceObj cenPoint circObj)
; Create a reference to AutoCAD
(setq acadObj (vlax-get-acad-object))
(princ acadObj)
(terpri)
; Create a reference to the current drawing
(setq docObj (vla-get-activedocument acadObj))
(princ acadObj)
(terpri)
(prompt "Determine current space")
(terpri)
; Get a reference to the current space
(if (= (vla-get-activespace docObj) acModelSpace)
(setq spaceObj (vla-get-modelspace docObj))
(setq spaceObj (vla-get-paperspace docObj))
)
(princ spaceObj)
(terpri)
(prompt "Start object definition")
(terpri)
; Create the center point for the circle
(setq cenPoint (vlax-make-safearray vlax-vbdouble '(0 . 2)))
(vlax-safearray-fill cenPoint '(5.0 5.0 0.0))
(princ cenPoint)
(terpri)
; Create a circle at 5,5 in a radius of 1.75
(setq circObj (vla-addcircle spaceObj cenPoint 1.75))
(princ circObj)
(terpri)
; Change the color of the circle to blue
(vla-put-color circObj 5)
(prompt "End object definition")
(terpri)
(princ)
)
When executed, the previous example displays the values of several variables using the PRINC
function and messages with the PROMPT
function at the Command prompt. The following is a sample of the output displayed:
Command: CREATECIRCLE-VL
#<VLA-OBJECT IAcadApplication 00000001400d6188>
#<VLA-OBJECT IAcadApplication 00000001400d6188>
Determine current space
#<VLA-OBJECT IAcadModelSpace 000000003285d808>
Start object definition
#<safearray...>
#<VLA-OBJECT IAcadCircle 0000000039fe7f88>
End object definition
Once you are done debugging, you would normally comment out or remove each statement that simply outputs the values and messages that the user wouldn't normally need to see. The downside to this though is that there could be hundreds of statements that you might need to comment out or remove each time you debug your program, what if there was a better approach to using messages to perform basic debugging. Over the years, I created a utility library specific for handling debug messages. This library allows you to display messages at the Command prompt or to a log file, and even disable the feature all at once without needing to comment or remove the debugging statements.
The debug utility library that I wrote consists of three global variables and five functions:
*GLOBAL-DEBUG-LOGFILE*
- Global variable that contains the log file name and location*GLOBAL-DEBUG-LOGGING*
- Global variable that can be used to determine if logging is enabled (T or nil)*GLOBAL-DEBUG-ENABLED*
- Global variable used to determine if debugging is enabled (T or nil)debug-mode
- Enables/disables the debug utilitydebug-get-enabled
- Gets the current status of the debug utility; enabled or disableddebug-get-logmode
- Gets the current status of log mode; enabled or disableddebug-get-logfile
- Gets the name and location of the log filedebug
- Accepts the value that should be displayed at the Command prompt or output to the log file
The following is what the actual function definitions look like from my debug utility library:
; Global debugging variables
; *GLOBAL-DEBUG-LOGFILE* - Log file name and location
; *GLOBAL-DEBUG-LOGGING* - Log enabled
; *GLOBAL-DEBUG-ENABLED* - Enable debugging (T or nil)
; Enable debugging - Example usage (debug-mode 2)
; 0 or nil - Disables debugging
; 1 or T - Enables debugging at the Command prompt
; 2 - Enables debugging and outputs to a log file
(defun debug-mode (enable / )
(cond
((or (= enable 0)(= enable nil))
(if (= (type *GLOBAL-DEBUG-LOGFILE*) 'FILE)
(progn
(close *GLOBAL-DEBUG-LOGFILE*)
(setq *GLOBAL-DEBUG-LOGFILE* nil)
)
)
(setq *GLOBAL-DEBUG-ENABLED* nil
*GLOBAL-DEBUG-LOGGING* nil
*GLOBAL-DEBUG-LOGFILE* nil)
)
((or (= enable 1)(= enable T))
(setq *GLOBAL-DEBUG-ENABLED* 1 *GLOBAL-DEBUG-LOGGING* 0)
(if (= (type *GLOBAL-DEBUG-LOGFILE*) 'FILE)
(progn
(close *GLOBAL-DEBUG-LOGFILE*)
(setq *GLOBAL-DEBUG-LOGFILE* nil)
)
)
)
((= enable 2)
(setq *GLOBAL-DEBUG-ENABLED* 1 *GLOBAL-DEBUG-LOGGING* 1)
(if (/= (type *GLOBAL-DEBUG-LOGFILE*) 'FILE)
(setq *GLOBAL-DEBUG-LOGFILE* (open (strcat (getvar "tempprefix") "global-debug.log") "a"))
)
)
)
(princ)
)
; Get the current state of debugging
(defun debug-get-enabled ( / )
*GLOBAL-DEBUG-ENABLED*
)
; Get the current logging state
(defun debug-get-logmode ( / )
*GLOBAL-DEBUG-LOGGING*
)
; Get the name of the current log file
(defun debug-get-logfile ( / )
*GLOBAL-DEBUG-LOGFILE*
)
; Debug function is a wrapper for the functionality
; of the princ and print functions. Feature is turned on or
; off with the debug-mode function.
(defun debug (value / )
(if (debug-get-enabled)
(if (/= (debug-get-logmode) 1)
(progn (princ value)(terpri))
(print value (debug-get-logfile))
)
)
(princ)
)
The following is a revision of the CreateCircle-VL
, shown earlier in this posting, which demonstrates the usage of the debug utility library compared to separate PRINC
and PROMPT
functions:
; Load COM environment
(vl-load-com)
; Enable debug mode
(debug-mode 1)
; Create a blue circle using Visual LISP
(defun c:CreateCircle-VL ( / acadObj docObj spaceObj cenPoint circObj)
; Create a reference to AutoCAD
(setq acadObj (vlax-get-acad-object))
(debug acadObj)
; Create a reference to the current drawing
(setq docObj (vla-get-activedocument acadObj))
(debug acadObj)
(debug "Determine current space")
; Get a reference to the current space
(if (= (vla-get-activespace docObj) acModelSpace)
(setq spaceObj (vla-get-modelspace docObj))
(setq spaceObj (vla-get-paperspace docObj))
)
(debug spaceObj)
(debug "Start object definition")
; Create the center point for the circle
(setq cenPoint (vlax-make-safearray vlax-vbdouble '(0 . 2)))
(vlax-safearray-fill cenPoint '(5.0 5.0 0.0))
(debug cenPoint)
; Create a circle at 5,5 in a radius of 1.75
(setq circObj (vla-addcircle spaceObj cenPoint 1.75))
(debug circObj)
; Change the color of the circle to blue
(vla-put-color circObj 5)
(debug "End object definition")
(princ)
)
When the revised code is loaded and executed, variable values and messages are displayed at the Command prompt. If you execute the statement (debug-mode 0)
at the Command prompt or change the (debug-mode 1)
statement to (debug-mode 0)
and reload the sample code, debugging is disabled and the variables and messages are no longer displayed at the Command prompt.
Hope this article makes you think differently about utilizing basic messages when debugging your custom programs.
Sincerely,
Lee
Comments