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.
@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?
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.
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".
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.
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
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.
@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
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.
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.
@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
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.
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.
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
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.
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
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.