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

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

59 REPLIES 59
Reply
Message 1 of 60
mmazanec
2777 Views, 59 Replies

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

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.

Labels (4)
59 REPLIES 59
Message 2 of 60
pbejse
in reply to: mmazanec


@mmazanec wrote:

I need help with an AutoLISP program I am editing/re-writing. 

 

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...


 

You mind sharing that lisp routine and a sample of the coordinate file? 

Message 3 of 60
mmazanec
in reply to: mmazanec

I knew this question would be coming, so I've been working on getting an "updated/improved" version of the original code working with some of the new functionality I've added.  I've managed to get the program quite close to what I am looking for, but I suspect there may be G-code programs that it would cause it to fail.  Certainly, there are G-code commands that this LISP routine does not interpret.

I am attaching the current LISP program (PolyNC4d.lps), the original LISP program (PolyNC.lsp) and a DATA file.  The current program is not able to plot a 0.0, 0.0 starting point for the first tool.  Also, a coordinate of 999, 999 in the G-code program would presently cause the program to stop.

Message 4 of 60
Sea-Haven
in reply to: mmazanec

Just a comment would rearrange the txt file a bit using a search and replace say in Word or Notepad++ preferred. 

 

(DATA POINTS for TESTING purposes)
T100 
X0.0 Y0.0
X1.0 Y3.0
X0.0 Y0.0
X1.0 Y-3.0
X-1.0 Y-10.0 
T101
X1.0 Y1.0 
X -6.0270 Y 6.0205 
X-1.0 Y1.0 
T104
X1.0 Y1.0 
X -6.0240 Y 6.0240 
X 7.0
X 8.0
Y 7.0
T105
X0.50 Y0.50
X-1.0 Y-1.0
X-2.0 Y-2.0
T106
X0.0 Y0.0
X1.0 Y-1.0
X0.0 Y0.0 
T107
X1.0 Y-3.0
X999 Y999
X0 Y0

This way the Txxx is used as a start new pline.

 

Added "to do list".

 

Message 5 of 60
mmazanec
in reply to: mmazanec

The intent of the program is to be able to correctly interpret G-code from several different machines and each machine might have some slight differences in the way the G-code is arranged.  Tttt already is what is used to tell the program to start a new pline.  The program is able to interpret a Tttt number on its own line or on a line with an Xxxx, Yyyy; and then after the first pline has been started, used Tttt and the other conditions (flag variables) to end the first pline and start the next pline.  This is not my issue.

 

As I stated in my original post, 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.  The LISP program had more issues when it was incorrectly using

((and (/= tno 0)(= flg1 1)(= xno 0)(= yno 0));REST OF TOOL NOS ONLY

as a condition for ending the first pline and starting the next pline when Tttt was on a line by itself because (= xno 0) and (= yno 0) (in other words x = 0 and y = 0) only when the function ncin is started, as while a file that is open is being read, the only way xno and yno would become 0 again is if the coordinates being read in were 0.  Therefore, the original program would default to the next condition

((and (/= tno 0)(= flg1 1));REST OF TOOL NOS WITH X OR Y

which would end the previous pline and then try to start a new pline with a new coordinate.

My fix/change to the condition

((and (/= tno 0)(= flg1 1)(= (car lnco) 999)(= (cadr lnco) 999));REST OF TOOL NOS ONLY

fixed this issue.

 

However, the coordinate X0.0, Y0.0 is still a problem for the first point of the first pline; and X999, Y999 as a coordinate would cause the program to skip that point coordinate at best and prehaps fault the program at worst.  I haven't done much testing and the recent CrowdStrike software issue occurrence has caused our IT department to have to reinstall my CAD applications.

Message 6 of 60
Sea-Haven
in reply to: mmazanec

Recognising "X999 Y999" is not a problem can be built in as a check and stop reading. 

 

What happens here, is T106 only 2 points ? 

T107 a single point ?

 

T106
X0.0 Y0.0
X1.0 Y-1.0
X0.0 Y0.0 
T107
X1.0 Y-3.0
X999 Y999
X0 Y0

Probably what is best is to post a dwg that matches the sample. Label The Txxx

Message 7 of 60
pbejse
in reply to: mmazanec

Based on the values on the DATA POINTS for TESTING purposes5.txt

 

as @Sea-Haven already stated, show us in drawing format what would be the ideal result granting all conditions are met, and what to do with the nil/null values, that would give us a better understanding.

 

 

 

 

Message 8 of 60
Moshe-A
in reply to: mmazanec

@mmazanec  hi,

 

To step into the 'shops' of your lisp author is a bit far then we can understand, each author has its own style of coding so i write my own version to read this data file and draw the polylines.

 

Attached RDF (Read Data File) command.

 

The main function (parsing_data) return list of the data points (plus the Txxx as to identify start of pline)

inside (parsing_data) a call to (read_data_file) which return the data read from the file as strings. this function is based on the wonderful function from Mr Lee Mac called (LM:str->lst) which return each record line as list of strings.

 

of course this program will work as long as you keep the data file as it is, any character here is significant.

 

enjoy

Moshe

 

 

(vl-load-com) ; load activex support

; Read Data File
(defun c:rdf (/ read_data_file parsing_data ; local functions
	        FNAME i pt lst0)

 ;; 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 read_data_file (fn / LM:str->lst ; local function
			        f lst1)

  (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 fn "r")))
   (progn
    (vlr-beep-reaction)
    (prompt (strcat "\nfail to open " fn " file for read.")) 
   ); progn
   (progn
    (while (setq record (read-line f))
     (setq lst1 (cons (LM:str->lst record " ") lst1))
    )
    (reverse lst1)
   )
  ); if 
 );  read_data_file

  
 (defun parsing_data (fn / isNumeric check_data ; local functions
		              item0 item1 j point^ points^)

 ; return T if ch is numeric
  (defun isNumeric (ch)
   (or
     (eq ch "-")
     (eq ch ".")
     (and (>= (ascii ch) 48) (<= (ascii ch) 57))
   )
  ); isNumeric

   
  ; return point^ if it's legalable to be add to datalist, otherwise nil
  (defun check_data ()
   (cond
    ((and
       (= (vl-list-length point^) 1)
       (eq (type (car point^)) 'STR)
     )
     point^
    ); case
    ((and
       (= (vl-list-length point^) 2)
       (vl-every 'numberp point^)
     )
     point^
    ); case
   ); cond
  ); check_data


  (foreach item0 (read_data_file fn)
   (setq j 0 point^ nil)
    
   (while (< j (vl-list-length item0)) ; parsing data record
    (setq item1 (nth j item0))

    (cond
     ((wcmatch item1 "T###")
      (setq points^ (cons item1 points^))
     ); case
     ((and
       (eq (substr item1 1 1) "X")
       (isNumeric (substr item1 2 1))
      )
      (setq point^ (cons (atof (substr item1 2)) point^))
     ); case
     ((and
       (eq (substr item1 1 1) "Y")
       (isNumeric (substr item1 2 1))
      )
      (setq point^ (cons (atof (substr item1 2)) point^))
     ); case
     ((and
       (eq item1 "X")
       (numberp (atof (nth (1+ j) item0)))
      )
      (setq point^ (cons (atof (nth (1+ j) item0)) point^))
     ); case
     ((and
       (eq item1 "Y")
       (numberp (atof (nth (1+ j) item0)))
      )
      (setq point^ (cons (atof (nth (1+ j) item0)) point^))
     ); case
    ); cond

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

   (if (setq point^ (check_data))
    (setq points^ (cons (reverse point^) points^))
   )
 
  ); foreach

  (reverse points^) ; return
 ); parsing_data

  
 ; here start c:rdf
 (setvar "cmdecho" 0)
 (command "._undo" "_begin")
  
 (setq FNAME "DATA POINTS for TESTING purposes5.txt") ; const
  
 (if (not (findfile FNAME))
  (progn
   (vlr-beep-reaction)
   (princ (strcat "\ncan not find " FNAME " file."))
  ); progn
  ; else
  (if (setq lst0 (parsing_data FNAME))
   (progn
    (setq i 0)
    (cond
     ((eq (type (nth i lst0)) 'STR)
      (setq i (1+ i))
      (command "._pline" "_None" (nth i lst0) "_width" 0 0)
      (setq i (1+ i))
      (setq lst0 (cddr lst0))
     ); case
     ( t
      (command "._pline" "_None" (nth i lst0) "_width" 0 0)
      (setq i (1+ i))
      (setq lst0 (cdr lst0))
     ); case
    ); cond

    (setq pt (nth i lst0))
    
    (while (< i (vl-list-length lst0))
     (cond
      ((eq (type pt) 'STR)
       (command "")
       (setq i (1+ i) pt (nth i lst0))
       (command "._pline" "_None" pt)
      ); case
      ((equal (distance pt '(999.0 999.0)) 0.0)
       (command "")
      ); case
      ( t
       (if (= (getvar "cmdactive") 1)
        (command "_None" pt)
       )
      ); case
     ); cond

     (setq i (1+ i) pt (nth i lst0))
    ); while

    ; make sure, end command if still running
    (if (= (getvar "cmdactive") 1)
     (command "")
    )
   ); progn
  ); if
 ); if

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

 

 

 

Message 9 of 60
john.uhden
in reply to: Moshe-A

@Moshe-A ,

Just for fun, what if a line contains...

X### Y### Z###

?

Or, how about just...

### ###

?

Or...

### ### ### ;; which could be either X,Y,Z or maybe N,E,Z

?

😖

John F. Uhden

Message 10 of 60
Moshe-A
in reply to: john.uhden

don't know, the OP said it's Tnnn

 

 

Message 11 of 60
Sea-Haven
in reply to: john.uhden

The Op is backward redoing a Gcode output. So file format should be consistent.

 

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.

Message 12 of 60
mmazanec
in reply to: pbejse

The data file I posted was just a sample data file.  The actual data files will be more varied.  I tried representing each of the possible (major) data line "formatting" occurrences in my sample file.  In reality, I will likely need to account for lower case "n", "g", "m", "x", "y", "z" and ”t” letters as well, as some of the G-code programs have lower case lettering.  The intent is to make this LISP routine robust enough that it doesn't have an unexpected error when reading actual data files.  Altering a/every data file to conform to some standard due to poor programming of the LISP routine would be illogical.

I am trying to fix two “errors” that occur with the LISP routine when it reads the data file I sent.  The first error is that the LISP routine misses the first XY coordinate, and I am assuming it is because that data point is 0.0, 0.0.  The second error is that LISP routine misses the 999, 999 XY coordinate, which could technically be a valid data point in a given G-code program.  Currently those are the only two errors present in the resulting CAD drawing that I am aware of.

Message 13 of 60
john.uhden
in reply to: mmazanec

@mmazanec wrote, " The actual data files will be more varied."

Ahah, that is why the files include the alpha prefix for every value in every line, right?

That should make it clear enough what to do with the data, that is with some foresight from you as to what varying combinations or order of data within each line would represent, e.g. line, point, text, or a series of file lines that represent the XY coordinates of polyline vertices.

John F. Uhden

Message 14 of 60
mmazanec
in reply to: john.uhden

Correct.  The alpha characters are the way G-code tells the CNC machine what command with what additional parameters the CNC machine is supposed to be running with the letter G being the main driving parameter.  I haven't gotten that far with improving this LISP routine, but, for example, a G90 command puts a CNC machine into absolute coordinates mode.  In this mode every XYZ corresponds to the actual position within the coordinate system.  G91 puts a CNC machine into incremental distance mode.  Right now this LISP routine processes everything as actual coordinates.

 

This is not a G-code forum, but G00 (G0) is for rapid motion and G01 (G1) is for cutting feed rate motion.  G02 establishes a mode for clockwise circular arc motion. G03 establishes a mode for counter-clockwise circular arc motion.  And it goes on from there with each CNC machine tending to follow the basics but also doing their own thing with some commands.  I don't know if I will ever get this routine to be able to correctly interpret other G-code  commands but there are a few that would be useful for my job related needs.  By adding the letters (alpha characters) I added, the LISP routine is now able to process G-code program lines it would have otherwise ignored.  So, it might not process the G-code program the way the given CNC machine would, but at least it will (should) now read such programs in their entirety, not faulting or aborting, and giving the user some inkling there is more to the G-code program that they will have to check on and where in the program that is (which tool "Tttt").

 

My next steps for this program will be figuring out a way to handle single point coordinate commands, as a pline would not work for a single point coordinate, and determining if I can use a 3Dpolyline for handling Z coordinates.  I have other work priorities that also require my time, so who know when I might have a chance to work on that.

Message 15 of 60
Moshe-A
in reply to: mmazanec

@mmazanec ,

 

up to the info you gave us on message #5, did you try my lisp command on message #8?

 

Moshe

Message 16 of 60
mmazanec
in reply to: Moshe-A

It did not work.  Like you, I do not have time to figure out your programming style and determine how you programmed it to work.  For one, it apparently looks for the data file I provided you by name and looks for it in a predetermined location without prompting the user to pick a file.  The LISP program I provided pops up a window allowing the user to select the data file.  Were you able to run the LISP I had sent you?

 

Looking at the LISP routine that you provided, it seems to still rely on using 999.0 999.0 for some sort of check.  The data files will be actual G-code programs that are formatted to run on the machines they came from and may have data points that are 999.0 999.0 anywhere in the G-code program.  Similarly, adding a 999.0 999.0 to the G-code program just to make the LISP routine work is also not acceptable.

 

You also state in your post:

"of course this program will work as long as you keep the data file as it is, any character here is significant"

As I indicated, the data file will change.

Message 17 of 60
john.uhden
in reply to: mmazanec

@mmazanec ,

This is like the reverse of creating cut files from AutoCAD to an Eastman automatic cutting machine (generally vinyl or cloth material).  After studying the Eastman .CMD file vs. the AutoCAD .DWG file, I was able to interpret the codes and write a .CMD file directly from AutoCAD with nothing but AutoLisp.  (Eastman provides a separate app (for an annual fee) to create a .CMD file from a .DXF.)  Working backwards is a lot tougher if you don't know all the G-Code codes.

John F. Uhden

Message 18 of 60
mmazanec
in reply to: john.uhden

The LISP routine I sent works and process a wide variety of G-code files without any issue after my LISP modifications that I tried to describe above.  What I was trying to better understand and get help with was to learn if there is a way that I could define my variables such that AutoCAD knows that they are a set type (like an integer or a string) even though the variable starts off as nil rather than as a specific value.

Message 19 of 60
john.uhden
in reply to: mmazanec

@mmazanec ,

You could set a list of variable types by string name, e.g.

(setq vartypes '(("X" . 'Real)("Y" . 'Real)("ID" . 'INT) ... ("ETC" . 'etc)))

Then you can confirm every element, e.g. "X234.123" ...

(if (= (setq what (cdr (assoc (substr element 1 1) vartypes)))(type (read (substr element 2))))

  (setq vartype what)

)

OR. maybe just...

(setq vartype (cdr (assoc (substr element 1 1) vartypes)))

Hmm, but that doesn't work for multiple character elements, like "ID."

Anyway, something like that, maybe ...

(or

   (setq vartype (cdr (assoc (substr element 1 1) vartypes)))

   (setq vartype (cdr (assoc (substr element 1 2) vartypes)))

)

John F. Uhden

Message 20 of 60
Moshe-A
in reply to: mmazanec

@mmazanec ,

 

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.

 

Moshe

 

 

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

Post to forums  

Forma Design Contest


AutoCAD Beta