Properly declaring variables and handling variables when they may be null/nil

Properly declaring variables and handling variables when they may be null/nil

mmazanec
Participant Participant
6,864 Views
59 Replies
Message 1 of 60

Properly declaring variables and handling variables when they may be null/nil

mmazanec
Participant
Participant

I need help with an AutoLISP program I am editing/re-writing.  I have tried searching the internet for how to address my issue without much success.  I believe that my problem is that I don’t know how to properly handle, account for, and set null values, particularly when it comes to having a list with some null values as part of that list.

 

I have a LISP program that will read a text file and convert that text file into data points (X Y points) that the LISP routine then used to draw a polyline.  The LISP program uses the read-line command to read in one line at a time the text string of that line, and then parses out that text letter by letter to obtain the X and Y coordinates and convert those text characters into real numbers that can be entered in as coordinates for a polyline.

 

The existing program uses a list variable containing the first list item of the X coordinate and the second list item of the Y coordinate, as well as other list variables.  There are instances where a coordinate may not exist and I need to be able to properly handle that null instance and not pass a zero (0), or not pass a nil to a function that cannot handle a nil, etc.  I am also not sure how the LISP routine is declaring variables and properly setting the variable type, or keeping variable types straight so that the wrong type of variable is not passed to a function expecting a different variable type. The LISP routine reads in G-code and converts the G-code XY coordinates into a graphical polyline representation of the tool path(s).  The original LISP routine uses 0.0 as the default “no value” X and Y coordinate values, and 999 as the set value for the non-coordinate comparison check to know when to stop.  This is problematic should the G-code contain 0.0 or 999 coordinate values.

0 Likes
6,865 Views
59 Replies
Replies (59)
Message 21 of 60

Sea-Haven
Mentor
Mentor

Can I ask the dumb question, why are you reverse engineering the gcode, is it because you have lost the original Dwg ? Or that your avoiding having to pay the original supplier of the dwg and subsequent Gcode. A phone call can be very cheap option in terms of time.

0 Likes
Message 22 of 60

Moshe-A
Mentor
Mentor

@mmazanec ,

 

in AutoLISP (and today with other modern languages) there is not type declaration\definition. a variable type is defined the moment you assign it a value otherwise it starts with nil but you can check a variable type with (type) function a moment you are about to use it:

 

(setq a 5)

(eq (type a) 'INT)

 

(setq b 6.87)

(eq (type b) 'REAL)

 

(setq s "abcdef")

(eq (type s) 'STR)

 

(setq pt '(5.80 4.20 0.0))

(eq (type pt) 'LIST)

 

Moshe

 

0 Likes
Message 23 of 60

komondormrex
Mentor
Mentor

imo,
you need to write a better parser for cnc-program that can read the entire cnc-program into a list of say dotted pairs (as John suggested), which you then process to every function code in it accordingly. 

0 Likes
Message 24 of 60

pbejse
Mentor
Mentor

It would ne nice if we can get a peek of what you consider the most complete data and describe each and every variation.

 

To be honest, i started reading thru every post by @mmazanec explaining the process, in the end I'm thinking let a sample drawing and a more complete data file speaks for it self.

0 Likes
Message 25 of 60

mmazanec
Participant
Participant

Hi John,

Thanks for your thoughts.  As I've worked on this more I'm starting to wonder if the addressing the variable types will get me to where I need to be, or if I just need to figure out a better way of using "flags".  Read my next post.

Mark

0 Likes
Message 26 of 60

mmazanec
Participant
Participant

@Sea-Haven
"The Op is backward redoing a Gcode output. So file format should be consistent."
Yes the G-code file format is consistent in that G-code has rules that it must follow.

 

"Notice in the 1st Txxx that there is lines with X 6.0 no Y, I guess this means use prior Y for missing value."
Correct. If there is only an X or only a Y then the previous/prior Y or X respectively is to be used.


@Moshe-A
"I do not think the absence of ability to choose data file is your problem and as it looks like (at least for now) no one here familiar with your CNC machine \ G-Code but still there are many experts here that can help you bring your wish to life if only you give us all the info needed."

 

I was trying to express that the main reason I could not run the LISP you sent is because I don't know where (what file path/directory) your LISP routine is looking for the file "DATA POINTS for TESTING purposes5.txt". The program gives me the message "can not find DATA POINTS for TESTING purposes5.txt" file. Our IT department has pretty tight controls on things, so there are only certain directories that I have write access to.
In the LISP routine that I provided the starting function is "(defun cnc ()..." . In order to start this LISP routine at the command line one would have to enter "(cnc)". I don't know if redefining the function as "(defun c:cnc ()..." would work.
I don't know what other information you want me to provide.


@Sea-Haven
"Can I ask the dumb question, why are you reverse engineering the gcode, is it because you have lost the original Dwg ?"

 

Some of our machines are older machines for which the records have been lost as the machines have moved from one building to another. So the programs exist on the CNC machine and may not exist anywhere else.
Being able to graphically represent the toolpaths with polylines in AutoCAD is just one additional tool in being able to fix or troubleshoot a CNC machine program when appropriate.


@komondormrex
"you need to write a better parser for cnc-program that can read the entire cnc-program into a list of say dotted pairs (as John suggested), which you then process to every function code in it accordingly."

 

This would have some advantages but I don't even know how to go about this at this point. Setting flags in the LISP routine to indicate that a specific command has been requested is the way I believe I might be able to incorporate more functionality at some point.
G-code was developed back in the infancy of computers and computer numeric control (CNC). As such, many CNC machines were only capable of processing one line of code at a time.
Again though, I was just looking for a way to handle the first X0.0 Y0.0 and the X999 Y999 at this stage with this post. I believe I have since figured out a way to handle the possibility of X999 Y999 being a valid data point in a G-code program.


@pbejse
"It would ne nice if we can get a peek of what you consider the most complete data and describe each and every variation.
To be honest, i started reading thru every post by @mmazanec explaining the process, in the end I'm thinking let a sample drawing and a more complete data file speaks for it self."

 

G-code is a programming language just like LISP is a programming language. People can program G-code differently for a CNC machine and still get the same result for the finished processed part. With that said, just like with LISP, there are rules that G-code follows, and so as long as the G-code program follows those rules, it will run on the CNC machine.
To describe each and every variation would be like describing each and every variation for how to program a LISP routine. Ideally, you should be able to pull anyone's valid G-code program from the internet and use it as a data file for the LISP routine I provided. The routine may not understand everything, and I am not asking it to, but ideally it should not fault out or miss XY points before reaching the end of the G-code program.
On a related note, rules can change, just like with LISP, sometime Autodesk changes commands within AutoCAD and that can break someone's existing LISP routine. Also, different machines might have slightly different forms of G-code, but again, I was not asking for help with making this LISP routine do everything. I was just trying to figure out how to better handle the X0.0 Y0.0 issue I found with the routine skipping that initial data point because the programs start-up values are 0.0, and the X999 Y999 issue I found because the program was using 999 as an end indicator of sorts. As I indicated above, I believe I have since fixed the 999 issue.

Message 27 of 60

john.uhden
Mentor
Mentor

@mmazanec ,

Ya know, just like I did with the Eastman cutter, I'll bet I could figure it all out if I had an AutoCAD drawing and the G-code output from it, but not too complex.

John F. Uhden

0 Likes
Message 28 of 60

john.uhden
Mentor
Mentor

@mmazanec ,

Unless there is a consistent sequence of data, as in X Y Z, then I think the alpha ID codes are significant or even required.

John F. Uhden

0 Likes
Message 29 of 60

mmazanec
Participant
Participant

When you say "IDs", what are you referring to?  Are you referring to the alpha characters before the numerical characters, namely the G-codes themselves?  If so, then yes, those alpha characters are significant.  At this time, the LISP routine only knows what to do with the T X and Y characters.  The other characters are read in, but nothing else is done with them presently other than allowing the routine to skip them as irrelevant until getting to the next T X and Y characters.  The parenthesis "(" and ")" are used to indicate comments in G-code that the machine can ignore, so my program flags comments allowing anything inside the parenthesis to pass through without being interpreted by the LISP routine.

 

Some G-code programs might have spaces at the start of a line or between the characters, some might not, so my revision allows spaces to be passed without causing the routine to produce unwanted results.  My routine will accept and properly handle spacing such as  X1.0Y1.0T1;  X2.0 Y2.0; X 1.0 Y 3.0; X0 Y2; X1 Y1 whereas before it would not.

 

Some G-code programs start with numbered lines, some don't.  A G-code program could look like the following (I'm not saying the following G-code program looks nice, but it obeys the rules):

 

(Cut/Make a triangle using Tool 1)
N10 G90 X1.0Y1.0 T1
N20 X2.0 Y1.0 (Y was 2.0)
N30 G70 X 1.0 Y 3.0
N40 X1 Y1
(Cut/Make a square using Tool 20)
(First corner starts at point x=0 y=3)
N50 T20
N60 (I am tired of numbering lines with N codes)
X0Y3 (Starting corner)
X2Y3 (second corner)
Y5 (MOVE TO CORRDINATE X2 Y5)
X0 (MOVE TO CORRDINATE X0 Y5)
Y3 (MOVE TO CORRDINATE X0 Y3 to close the square)

(Hey, now my G-code programs have Z depths?)
(Most G-code programs have Z depths)
(What is going to happen with this LISP routine?)
(Will the program fault if it sees a Z coorinate value?)
(Lets try cutting/making a diamond at some Z values.)

N1000 T123 (LET'S USE TOOL 123)
N1010 X3Y3Z3 (LET'S START OUR DIAMOND AT X=3 Y=3)
N1020 X5Y1Z3
N1030 X7 Y3 Z1234
N1040 X5 Y5 Z5678
N1050 X3 Y3 Z0
(THE Z DOESN'T DO ANYTHING RIGHT NOW)
(BUT THE LISP ROUTINE STILL GETS THE X AND Y COORDINATES CORRECT)

0 Likes
Message 30 of 60

john.uhden
Mentor
Mentor

Thank you, @mmazanec ,

By "ID" I meant the alpha characters X, Y, and T.  Without them and without spaces you would have no idea what the concatenation of numerals would represent.

That helps a bit, but offhand I can't tell if there are any "tool-up"s.  The Eastman has frequent tool-ups if the blade finishes a piece but the next one doesn't start at the previous point.  It also knows to lift its head if the next segment is along the edge of the material.  Does the CNC machine ever lift its tool?

Anyway, it sounds like you have things covered.  So remind me of the troubles you are having, i.e. how we can help.

John F. Uhden

0 Likes
Message 31 of 60

komondormrex
Mentor
Mentor

which is default when a press is switched on? g90 or g91?

0 Likes
Message 32 of 60

mmazanec
Participant
Participant

Hi @john.uhden

 

Different machines handle tool changes (moving the tool up and/or out of the way) differently.  Some have to be told with a Z coordinate to move up.  Some have to be told to go to a specific location and perhaps told to avoid workholding clamps or other obstacles before performing a tool change, but if so, this is usually simply done by using XYZ coordinates to tell the tool where to go.  Some machines are smart enough to know that when it sees a T command the tool is supposed to retract, move to where the tool needs to go to get changed, and then change the tool.  Some machines are even “smarter” or more functional in that they are able to stage the next tool in anticipation of the next tool change.  The LISP routine I provided is not capable of interpreting any of this, but being able to properly interpret and handle Z coordinates and using those Z coordinates to turn the tool path into a 3D polyline would be one step closer to this.

 

The issue I was trying to resolve was that because the variables “xno” and “yno” are set to 0.0 initially, if the very first data point for the very first tool actually is X0.0Y0.0 then the LISP routine skipped that point. This is because the first time the LISP routine detects a T code it starts a pline command.  The second time the routine detects a T code it ends the first pline command and then starts a new pline command.  However, I believe I have now resolved that issue in the same way that I resolved the 999 issue.

 

Mark

0 Likes
Message 33 of 60

mmazanec
Participant
Participant

Hi @komondormrex

 

As far as I know, most machines start in G90 mode, absolute coordinates mode.  In this mode every XYZ corresponds to the actual position within the coordinate system.  Right now the LISP routine I provided only processes everything as actual position coordinates and only interprets the XY values ignoring the Z values.

It would be cool if the LISP routine was able to correctly process G90 and G91 commands switching between absolute coordinates mode and incremental distance mode because this is very common in G-code programs.  The program I provided cannot do that a present.

 

Mark

0 Likes
Message 34 of 60

john.uhden
Mentor
Mentor

@mmazanec ,

That sounds like the days of DCA (David C. Arnold) and Softdesk (which later was acquired by Autodesk and marketed as Land Development Desktop, the forerunner of Civil 3D).  Every drawing had a base point which was likely to be other than 0,0,0.  All that was required to use AutoLisp as normal was to translate points.  No big deal.  I dealt with that a lot until the industry decided it made things too complex and returned to using 0,0,0.  Thank goodness.  All that translation added up to a waste of processing time.

John F. Uhden

0 Likes
Message 35 of 60

john.uhden
Mentor
Mentor

@mmazanec ,

It sounds as though you are resolving your own issues.  That's good to hear!

John F. Uhden

0 Likes
Message 36 of 60

Moshe-A
Mentor
Mentor

@mmazanec  hi,

 

Here is my C:RDF command to read "DATA POINTS for TESTING purposes5.txt" file and draw the polylines according the T### tool. 

 

The program starts with opening AutoLISP dialog box to pick data file than (read_data_file) is called where the file is opened. each record line than send to (LM:str->lst) function by Lee Mac which return as list of strings to caller which return two dimensional list (list of lists). in main program the data list is decipher

i must admit, the fact that the data records are not consistent, gave me to hard time but at last got success 😀 (only for T###, X , Y)

 

Study carefully the code and if you have more questions, i will be here to help.

if you feel strong enough in AutoLISP, you are invite to take it from here and add all the G-codes you need. 

 

there is no need to deeply understand (LM:str->lst) function only to know what it return, the same with (_isNumeric) only to know that is return T if the passed str is numeric number or nil if not.

 

enjoy,

Moshe

 

 

 

(vl-load-com) ; load activex support

; Read Data File
(defun c:rdf (/ read_data_file _isNumeric draw_pline ; local functions
	        ncfile lst0 clr flag draw_mode i j item0 item1 dx0 dy0 dx1 dy1)

 ; return two dimensional list
 (defun read_data_file (fname / LM:str->lst ; local function
			     f lst)
 ;; String to List  -  Lee Mac
 ;; Separates a string using a given delimiter
 ;; str - [str] String to process
 ;; del - [str] Delimiter by which to separate the string
 ;; Returns: [lst] List of strings

  (defun LM:str->lst ( str del / pos )
    (if (setq pos (vl-string-search del str))
        (cons (substr str 1 pos) (LM:str->lst (substr str (+ pos 1 (strlen del))) del))
        (list str)
    )
  ); LM:str->lst

  (if (not (setq f (open (findfile fname) "r")))
   (progn
    (vlr-beep-reaction)
    (prompt (strcat "\nfail to open " fname " file for read.")) 
   ); progn
   (progn
    (while (setq record (read-line f))
     (setq lst (cons (LM:str->lst record " ") lst))
    )
    (reverse lst)
   )
  ); if 
 );  read_data_file

  
 ; return T if str is a number
 (defun _isNumeric (str)
  (vl-every
    (function
      (lambda (n)
       (or
	(member n '(45 46 59))
        (and (>= n 48) (<= n 57))
       )
      ); lambda
    ); function
    (vl-string->list str)
  ); vl-every
 ); _isNumeric

  
 (defun draw_pline ()
  (if (and dx1 dy1)
   (progn
    (command "None" (list dx1 dy1))
    (setq dx0 dx1 dy0 dy1) ; save current point
    (setq dx1 nil dy1 nil)
   ); progn
  ); if
 ); draw_pline
 
  
 ; here start c:rdf
 (setvar "cmdecho" 0)
 (command "._undo" "_begin")
  
 ; (if (not (setq ncfile (getfiled "NC Files" "C:\\Users\\user\\Downloads\\" "txt" 8)))
 (if (not (setq ncfile (getfiled "NC Files" "R:\\AMADA\\" "txt" 8)))
  (progn  
   (vlr-beep-reaction)
   (princ (strcat "\ncan not open " ncfile " file."))
  ); progn
  ; else
  (if (setq lst0 (read_data_file ncfile)) ; two dimensional list
   (progn
    (setq clr 0 i 0 flag t)
    (while (and flag (< i (vl-list-length lst0))) ; iterate OUT LOOP
     (setq item0 (nth i lst0) j 0 draw_mode t)
     (while (and flag (< j (vl-list-length item0))) ; iterate IN LOOP
      (setq item1 (nth j item0))
      (cond
       ((and (eq (substr item1 1 4) "X999")
	     (eq (substr (nth (1+ j) item0) 1 4) "Y999")  ; REST OF TOOL NOS ONLY
	)
	(draw_pline)
	(if (/= (getvar "cmdactive") 0) (command ""))
        (setq flag nil draw_mode nil)
       ); case
       ((wcmatch item1 "T###")                      ; TOOL NO ONLY
	(draw_pline)
        (if (/= (getvar "cmdactive") 0) (command ""))
        (command "._layer" "_Make" (substr item1 2) "_Color" (setq clr (1+ clr)) "" "")
        (command "._pline")
       ); case
       ((and
	  (eq item1 "X")                            ; TOOL WITH ONLY X
          (_isNumeric (nth (1+ j) item0))           ; validate number
	  (eq (nth (+ j 2) item0) ";")
	)
        (setq dx1 (atof (nth (1+ j) item0)))
	(setq dy1 dy0)
       ); case
        ((and
	  (eq item1 "Y")                            ; TOOL WITH ONLY Y
          (_isNumeric (nth (1+ j) item0))           ; validate number
	  (eq (nth (+ j 2) item0) ";")
	)
	(if (not dx1)
 	 (setq dx1 dx0)
	)
        (setq dy1 (atof (nth (1+ j) item0)))	 
       ); case
       ((and
         (eq item1 "X")        	    		    ; TOOL WITH X & Y
	 (_isNumeric (nth (1+ j) item0))            ; validate number
	 (>= (vl-list-length item0) 4)
	 (eq (nth (+ j 2) item0) "Y")
	 (_isNumeric (nth (+ j 3) item0))           ; validate number	 
	)
        (setq dx1 (atof (nth (1+ j) item0)))
	(setq j (1+ j))
       ); case
       ((and
         (eq (substr item1 1 1) "X")        	    ; TOOL WITH X
	 (_isNumeric (substr item1 2))              ; validate number
	)
        (setq dx1 (atof (substr item1 2)))
       ); case
       ((and
         (eq (substr item1 1 1) "Y")        	    ; TOOL WITH ONLY Y
	 (_isNumeric (substr item1 2))              ; validate number
	)
        (setq dy1 (atof (substr item1 2)))
       ); case 
      ); cond

      (setq j (1+ j))
       
     ); while

     (if draw_mode (draw_pline))
     (setq i (1+ i))
      
    ); while

    (if (/= (getvar "cmdactive") 0) (command "")) ; finish pline command
    
   ); progn
  ); if
 ); if

 (command "_undo" "_end")
 (setvar "cmdecho" 1)
  
 (princ)
); c:rdf

 

 

 

0 Likes
Message 37 of 60

john.uhden
Mentor
Mentor

You've got a problem there, @Moshe-A .

At least one of the examples @mmazanec provided had no spaces, as in

X3Y4

So each line has to be dissected differently, perhaps by one character at a time, or by testing to see if there are any spaces in the line using (vl-string-search " " line)

If there are no spaces then you could pretreat the line via (vl-string-subst " Y" "Y" line) and then convert it into a list.

John F. Uhden

0 Likes
Message 38 of 60

Moshe-A
Mentor
Mentor

Sorry, worked according to data file posted on message #3. the CNC machine must be consistent, it can't change the format every day 😀

0 Likes
Message 39 of 60

john.uhden
Mentor
Mentor

@Moshe-A ,

The >, <, >=, and <= functions can take more than 2 arguments.

For example,

(and (>= n 48) (<= n 57))

could be written as just

(<= 48 n 57)

because 48 is <= n, and n is <= 57

depending on the order of evaluation.

John F. Uhden

0 Likes
Message 40 of 60

Moshe-A
Mentor
Mentor

@john.uhden ,

 

thanks john, never notice that (lisp is awesome language)

0 Likes