I am trying to create an applicaiton that will verify that a large text file used far a CNC machince has all of the operations in order (someone may have edited the file by mistake) I wish to bubble sort the lines of the file while ignroing other lines. I really stuck on how to set up and handle this program. Here is what I have thus far (just started).
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////

Imports System.IO
Public Class Form1
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Call FilterFile("mydoc.txt", "N")
        MessageBox.Show("finished")
        Me.Close()

    End Sub

    Function FilterFile(ByVal sFile As String, ByVal sFilter As String) As Boolean
        Dim lines As New List(Of String)
        Try

            Using sr As New StreamReader(sFile)
                While Not sr.EndOfStream
                    lines.Add(sr.ReadLine)
                End While
            End Using
            For x As Integer = lines.Count - 1 To 0 Step -1
                If lines(x).Contains(sFilter) Then
                    lines.Item(x) = lines.Item(x) & vbLf & "  Compare this line"

                    '''''Bubble Sort Contents here...'''

                End If
            Next

                Using sw As New StreamWriter(sFile)
                    For Each line As String In lines
                        sw.WriteLine(line)
                    Next
                End Using
                Return True
        Catch ex As Exception : Return False : End Try
    End Function

///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////

Here is a sample of the way the text file is formatted:

; 
; ====== PROGRAM JUMP IN ======
IF LAST_TOOL==8002
  T8002
  GOTOF T_8002
ENDIF
; =============================
; 
%_N_OP080_01_MPF
;$PATH=/_N_WKS_DIR/_N_OP080_WPD
;###########################################################
;###########################################################
; 
N1000 G71
N1010 $AC_TIMER[1]=0         ;TOTAL CYCLE-TIME + LOADING AND UNLOADING
N1020 $AC_TIMER[2]=0         ;SINGLE TIME EACH TOOL
N1030 $AC_TIMER[3]=0         ;TOTAL CYCLE-TIME
;
N1130 $AC_TIMER[2]=-1
N1140 R70=R70+1
N1150 R[R70]=$AC_TIMER[2]
N1160 $AC_TIMER[2]=0
; 
; *** insert machine startup code here ***
N100 
; 
;===================== PROGRAM JUMP IN =====================
N1040 IF (LAST_TOOL <> 0)
N1050   WHEN TRUE DO $R21=1
N1060   T1=LAST_TOOL
N1070   GOTOF (<<"T_"<<LAST_TOOL)
N1080 ENDIF
N1090 WHEN TRUE DO $R21=0
;===========================================================
; 
N1100 T_801:
N1110 M21 M11
N1120 G0 G508 X0.0 Y0.0 A=DC(-90.0) B=DC(0.0) M20 M10
; 
;###########################################################

The lines N#### need to be re-ordered appropriately (notice that this example is in fact out of order)

Thanks for the help in advance, I am attempting to save some tooling engineers headaches from the CNC equipment crashing.

Recommended Answers

All 17 Replies

I've always been a big fan of letting the computer do as much work as it can. Don't bother writing the bubble sort. The following code uses a sorted dictionary. The key is the line number (with the "N" removed) and the value is the rest of the line. No matter what order you add the lines, when you step through the keys you will always get them in sorted order. That is why I made the key Integer. Not all of your code lines are N####. One is N### (three digits). Note that When I output the lines to the ListBox I ensure that the #### portion is four digits.

Public Class Form1

    Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click

        Dim sd As New SortedDictionary(Of Integer, String)

        For Each line As String In System.IO.File.ReadAllLines("D:\temp\sampleCode.txt")

            Dim linenumb As String      'the N#### portion of the line
            Dim linecode As String      'everything other than N####

            If line.StartsWith("N") Then
                linenumb = Split(line)(0).Substring(1)
                linecode = line.Substring(Len(linenumb) + 1)
                sd.Add(CInt(linenumb), linecode)
            End If

        Next

        For Each linenumb As Integer In sd.Keys
            ListBox1.Items.Add(linenumb.ToString("0000") & sd(linenumb))
        Next

    End Sub

End Class

This was fantastic, evaluating the mechanics of this section of code really, really helped out a bunch!!! You deserve a high five Reverend Jim, thank you for your time!

Are you sure that sorting based on N#### is really the best logic?
In your sample file, there appears to be condition statements defined after the N####.
i.e.

N1040 IF (LAST_TOOL <> 0)
N1050 WHEN TRUE DO $R21=1
N1060 T1=LAST_TOOL
N1070 GOTOF (<<"T_"<<LAST_TOOL)
N1080 ENDIF

A typo in the N#### followed by sorting could easily change the logic of the CNC program. Would it not be better to make your program a validator/editor that requires human input to check the logic when inconsistencies are found?

Yes this was a consideration, mostly these blocks (inbetween the semi-colons) are copied and pasted in chunks, so I a block with a statement would indeed need to stay together. As in the example above the last block (lines 36 - 38) would get sorted to start at the current line 19.

It sounds like the logic is alreagy becoming more complex than a simple sorting based on N###. I have put together a simple demo program, that offers two options. Button1 will look for inconsistencies based on N### and present the offending block to the user for editing. Button2 invokes a reordering logic based on N###, but this type of logic is prone to failure as there is no way a programmer can forsee every possible mistake a human can make in coding and automatically correct it.

Public Class Form1

   Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
      Dim fs As New IO.FileStream("code.txt", IO.FileMode.Open)
      Dim sr As New IO.StreamReader(fs)

      Dim sb As New System.Text.StringBuilder(CInt(fs.Length))
      Do Until sr.EndOfStream
         sb.AppendLine(sr.ReadLine)
      Loop
      sr.Close()
      With RichTextBox1
         .WordWrap = False
         .ScrollBars = RichTextBoxScrollBars.ForcedBoth
         .Text = sb.ToString
         .HideSelection = False
      End With
   End Sub

   Sub OrderOnN_Numbers()
      Dim ErrorExists As Boolean = False
      Dim lastNumber As Int32 = -1
      Dim currentNumber As Int32
      Dim BadnumberRef As LineNumberRef = Nothing

      Dim NumberIndices As New List(Of LineNumberRef)
      Dim lines() As String = RichTextBox1.Lines
      Dim l As String
      Dim index As Int32
      For i As Int32 = 0 To lines.Count - 1
         l = lines(i).Trim
         If l.StartsWith("N") Then
            index = l.IndexOf(" ", 1)
            currentNumber = -1
            If index = -1 Then
               Int32.TryParse(l.Substring(1), currentNumber)
            Else
               Int32.TryParse(l.Substring(1, index - 1), currentNumber)
            End If

            If currentNumber <> -1 Then
               If lastNumber >= currentNumber Then 'error

                  If Not ErrorExists Then
                     BadnumberRef = New LineNumberRef(lastNumber, i)
                     With RichTextBox1
                        .SelectionStart = .GetFirstCharIndexFromLine(i)
                     End With
                  End If
                  ErrorExists = True

               Else
                  If Not ErrorExists Then
                     lastNumber = currentNumber
                     NumberIndices.Add(New LineNumberRef(lastNumber, i))
                  Else
                     'found end of bad numbers
                     Dim endbad As Int32 = RichTextBox1.GetFirstCharIndexFromLine(i)
                     RichTextBox1.SelectionLength = (endbad - RichTextBox1.SelectionStart)
                     Exit For
                  End If 'Not ErrorExists

               End If 'lastNumber >= currentNumber 

            End If 'currentNumber <> -1 
         End If
      Next i

      If ErrorExists Then
         If RichTextBox1.SelectionLength = 0 Then
            RichTextBox1.SelectionLength = (RichTextBox1.Text.Length - RichTextBox1.SelectionStart) + 1
         End If
         NumberIndices.Sort()
         Dim BadIndex As Int32 = NumberIndices.IndexOf(BadnumberRef)
         RichTextBox1.Cut()
         If BadIndex = 0 Then
            RichTextBox1.SelectionStart = 0
         Else
            RichTextBox1.SelectionStart = RichTextBox1.GetFirstCharIndexFromLine(NumberIndices(BadIndex - 1).LineNumber + 1)
         End If
         RichTextBox1.Paste()
         OrderOnN_Numbers()
      End If

   End Sub

   Private Class LineNumberRef
      Implements IEquatable(Of LineNumberRef)
      Implements IComparable(Of LineNumberRef)

      Public N_Number As Int32
      Public LineNumber As Int32
      Public Sub New(ByVal N As Int32, ByVal Line As Int32)
         Me.N_Number = N : Me.LineNumber = Line
      End Sub

      Public Function Equals1(ByVal other As LineNumberRef) As Boolean Implements System.IEquatable(Of LineNumberRef).Equals
         Return Me.N_Number = other.N_Number
      End Function

      Public Function CompareTo(ByVal other As LineNumberRef) As Integer Implements System.IComparable(Of LineNumberRef).CompareTo
         Return Me.N_Number.CompareTo(other.N_Number)
      End Function
   End Class

   Private Function ValidateN_Numbers() As Boolean
      Dim ErrorExists As Boolean = False
      Dim lastNumber As Int32 = -1
      Dim currentNumber As Int32
      Dim NumberIndices As New Dictionary(Of Int32, Int32)
      Dim l As String
      Dim index As Int32
      For i As Int32 = 0 To RichTextBox1.Lines.Count - 1
         l = RichTextBox1.Lines(i).Trim
         If l.StartsWith("N") Then
            index = l.IndexOf(" ", 1)
            currentNumber = -1
            If index = -1 Then
               Int32.TryParse(l.Substring(1), currentNumber)
            Else
               Int32.TryParse(l.Substring(1, index - 1), currentNumber)
            End If

            If currentNumber <> -1 Then
               If lastNumber >= currentNumber Then 'error

                  ''highlight line
                  If Not ErrorExists Then
                     With RichTextBox1
                        .SelectionStart = .GetFirstCharIndexFromLine(i)
                     End With
                  End If
                  ErrorExists = True

               Else
                  If Not ErrorExists Then
                     lastNumber = currentNumber
                     NumberIndices.Add(lastNumber, i)
                  Else
                     'found end of bad numbers
                     Dim endbad As Int32 = RichTextBox1.GetFirstCharIndexFromLine(i)
                     RichTextBox1.SelectionLength = (endbad - RichTextBox1.SelectionStart)
                     Exit For
                  End If 'Not ErrorExists

               End If 'lastNumber >= currentNumber 

            End If 'currentNumber <> -1 
         End If
      Next i
      If ErrorExists And RichTextBox1.SelectionLength = 0 Then
         RichTextBox1.SelectionLength = (RichTextBox1.Text.Length - RichTextBox1.SelectionStart) + 1
      End If
      RichTextBox1.ScrollToCaret()
      Return ErrorExists
   End Function

   Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
      Button1.Enabled = False
      If Not ValidateN_Numbers() Then MsgBox("Validates")
      RichTextBox1.Focus()
      Button1.Enabled = True
   End Sub

   Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
      Button2.Enabled = False
      OrderOnN_Numbers()
      RichTextBox1.SelectionStart = 0
      RichTextBox1.Focus()
      Button2.Enabled = True
   End Sub
End Class

Would you please add some comments/documentation as to what this code is doing other than "look for inconsistencies" and "invokes a reordering logic"? Your code is appreciated but it would be even better if the mechanics were explained.

Would you please add some comments/documentation as to what this code is doing other than "look for inconsistencies" and "invokes a reordering logic"? Your code is appreciated but it would be even better if the mechanics were explained.

Jim,
I did not realize that it was as requirement of this site to post fully documented code.

I also did not realize that I was presenting anything that complex and it was not my intent to offer that code as a solution, but rather an example to continue the discussion that I had started with the OP that a simple sort was probably not a good idea as it could easily make a bad situation worse.

Oh well, live and learn. In complying with your request, I found a few logic mistakes that are now corrected in the code below. I suppose that I should state that to use this code one should open a new WinForm project in visual studio. Add two buttons and a richtextbox to Form1. Open the Form1.vb file in code view and replace the code found there with that shown below.

Public Class Form1

   Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
      'Read in the CNC code file
      Dim fs As New IO.FileStream("code.txt", IO.FileMode.Open)
      Dim sr As New IO.StreamReader(fs)

      'Read lines into a stringbuilder. 
      'This is a waste of memory, but will speed up loading
      'could prevent RTB from repainting and append directly to RTB, but do not want to 
      'confuse OP with extraneous performance boosting technique
      Dim sb As New System.Text.StringBuilder(CInt(fs.Length))
      Do Until sr.EndOfStream
         sb.AppendLine(sr.ReadLine) 'maintains EOL character for RTB
      Loop
      sr.Close() 'done with file stream
      With RichTextBox1
         'will be using line based positioning in the code, i.e.: GetFirstCharIndexFromLine
         'so I do not want wordwrap generating additional lines
         .WordWrap = False
         .ScrollBars = RichTextBoxScrollBars.ForcedBoth
         .Text = sb.ToString 'load RTB with CNC program
         .HideSelection = False 'Leave selection highlighted when focus leaves RTB
      End With
   End Sub

   ''' <summary>
   ''' OrderOnN_Numbers will attempt to re-order the CNC program if N####'s 
   '''   are found out of sequence. It will move multiple lines at a time.
   ''' </summary>
   ''' <remarks></remarks>
   Sub OrderOnN_Numbers()
      Dim ErrorExists As Boolean = False 'Method flag indicating improper N#### sequence
      Dim lastNumber As Int32 = -1 'Last N#### number read. Initialize to invalid value
      Dim currentNumber As Int32 ' current N#### read


      Dim NumberthatGeneratedError As Int32
      'create a list of linenumberrefs to store N####, RTB physical line # pairs
      Dim NumberIndices As New List(Of LineNumberRef)

      Dim lines() As String = RichTextBox1.Lines 'Pull RTB physical lines of text
      Dim index As Int32 'temp var to aid in extracting integer portion of N#####

      For i As Int32 = 0 To lines.Count - 1
         lines(i) = lines(i).Trim 'eliminate leading and trailing spaces

         If lines(i).StartsWith("N") Then 'found a N####?
            index = lines(i).IndexOf(" ", 1) 'find end of N####
            currentNumber = -1 'initialize potential N#### to invalid value

            If index = -1 Then 'no end found, may be just N### on line
               Int32.TryParse(lines(i).Substring(1), currentNumber)
            Else
               Int32.TryParse(lines(i).Substring(1, index - 1), currentNumber)
            End If

            If currentNumber <> -1 Then 'Parsing succeeded, have N####

               If lastNumber >= currentNumber Then 'ie:  N1020 > N100
                  'N#### out of sequence

                  If Not ErrorExists Then
                     NumberthatGeneratedError = currentNumber
                     With RichTextBox1 'start selection of misplaced lines
                        .SelectionStart = .GetFirstCharIndexFromLine(i)
                     End With
                  End If
                  ErrorExists = True ' set error condition

               Else 'currentnumber is in sequence

                  If ErrorExists Then
                     'this allows the finding of a range of lines that are out of sequence
                     'if the currentnumber is in sequence with the last set lastNumber,
                     'found end of bad numbers
                     'set the RTB selection length to grab this range
                     Dim endbad As Int32 = RichTextBox1.GetFirstCharIndexFromLine(i)
                     RichTextBox1.SelectionLength = (endbad - RichTextBox1.SelectionStart)
                     Exit For 'stop scanning lines
                  Else
                     'keep processing lines
                     lastNumber = currentNumber
                     'store N#### location
                     NumberIndices.Add(New LineNumberRef(lastNumber, i))
                  End If 'ErrorExists

               End If 'lastNumber >= currentNumber 

            End If 'currentNumber <> -1 
         End If 'lines(i).StartsWith("N")
      Next i

      'reached this point either by finding back block of lines, or finished processing all lines

      If ErrorExists Then 'have a bad block of lines
         If RichTextBox1.SelectionLength = 0 Then
            'we where in a bad block and reached the end of the file
            'select all lines from start of bad to the EOF
            RichTextBox1.SelectionLength = (RichTextBox1.Text.Length - RichTextBox1.SelectionStart) + 1
         End If

         Dim textToMove As String = RichTextBox1.SelectedText 'best not to use clipboard via a cut & paste
         RichTextBox1.SelectedText = "" 'delete it 

         'need to determine where to paste this now
         'sort the stored N####, linenumber pairs in ascending order
         NumberIndices.Sort()
         'use NumberthatGeneratedError to determine the paste location
         'get the index of NumberthatGeneratedError in the newly sorted list
         'note that for search purpose, the linenumber part of the LineNumberRef is irrelevant
         'Binary search will return either a match or negative value that is the bitwise complement 
         'of the index of the value that is greater than value.
         'the value should not be in the list, but it could be do to the nature of this exercise
         Dim BadIndex As Int32 = NumberIndices.BinarySearch(New LineNumberRef(NumberthatGeneratedError, 0))

         If BadIndex < 0 Then
            BadIndex = Not BadIndex
         End If

         If NumberIndices(BadIndex).LineNumber = 0 Then
            'can not place before the start of the file
            RichTextBox1.SelectionStart = 0
         Else
            'place it on the 1st line before the N#### that is greater than it
            RichTextBox1.SelectionStart = _
               RichTextBox1.GetFirstCharIndexFromLine( _
                  NumberIndices(BadIndex).LineNumber - 1)
         End If
         RichTextBox1.SelectedText = textToMove ' paste it back in
         'scan the file again for misplaced lines
         'if none are found processing will stop
         '1st do some cleanup, no need to hold this memory
         lines = Nothing
         NumberIndices = Nothing
         textToMove = Nothing
         OrderOnN_Numbers()
      End If

   End Sub

   ''' <summary>
   ''' Helper class to define a structure consisting of the N#### and the line number in
   '''  the file that it currently is located
   ''' </summary>
   ''' <remarks></remarks>
   Private Class LineNumberRef
      Implements IEquatable(Of LineNumberRef) 'allows for testing equality
      Implements IComparable(Of LineNumberRef) 'allows comparisons needed for sorting

      Public N_Number As Int32
      Public LineNumber As Int32
      Public Sub New(ByVal N As Int32, ByVal LineNumber As Int32)
         Me.N_Number = N : Me.LineNumber = LineNumber
      End Sub

      ''' <summary>
      ''' stucture is considered equal if N#### is the same
      ''' </summary>
      ''' <param name="other"></param>
      ''' <returns></returns>
      ''' <remarks></remarks>
      Public Function Equals1(ByVal other As LineNumberRef) As Boolean Implements System.IEquatable(Of LineNumberRef).Equals
         Return Me.N_Number = other.N_Number
      End Function

      ''' <summary>
      ''' sorting of the these structures will be done based on N#### integer part
      ''' </summary>
      ''' <param name="other"></param>
      ''' <returns></returns>
      ''' <remarks></remarks>
      Public Function CompareTo(ByVal other As LineNumberRef) As Integer Implements System.IComparable(Of LineNumberRef).CompareTo
         Return Me.N_Number.CompareTo(other.N_Number)
      End Function
   End Class

   ''' <summary>
   ''' This routine finds the first block of code in the CNC program that 
   ''' is out of sequence.  It selects the block and returns control to the user   
   ''' </summary>
   ''' <returns></returns>
   ''' <remarks></remarks>
   Private Function ValidateN_Numbers() As Boolean

      Dim ErrorExists As Boolean = False 'Method flag indicating improper N#### sequence
      Dim lastNumber As Int32 = -1 'Last N#### number read. Initialize to invalid value
      Dim currentNumber As Int32 ' current N#### read

      'create a list of linenumberrefs to store N####, RTB physical line # pairs
      Dim NumberIndices As New List(Of LineNumberRef)

      Dim lines() As String = RichTextBox1.Lines 'Pull RTB physical lines of text
      Dim index As Int32 'temp var to aid in extracting integer portion of N#####

      For i As Int32 = 0 To lines.Count - 1
         lines(i) = lines(i).Trim 'eliminate leading and trailing spaces

         If lines(i).StartsWith("N") Then 'found a N####?
            index = lines(i).IndexOf(" ", 1) 'find end of N####
            currentNumber = -1 'initialize potential N#### to invalid value

            If index = -1 Then 'no end found, may be just N### on line
               Int32.TryParse(lines(i).Substring(1), currentNumber)
            Else
               Int32.TryParse(lines(i).Substring(1, index - 1), currentNumber)
            End If

            If currentNumber <> -1 Then 'Parsing succeeded, have N####

               If lastNumber >= currentNumber Then 'ie:  N1020 > N100
                  'N#### out of sequence

                  If Not ErrorExists Then
                     With RichTextBox1 'start selection of misplaced lines
                        .SelectionStart = .GetFirstCharIndexFromLine(i)
                     End With
                  End If
                  ErrorExists = True ' set error condition

               Else 'currentnumber is in sequence

                  If ErrorExists Then
                     'this allows the finding of a range of lines that are out of sequence
                     'if the currentnumber is in sequence with the last set lastNumber,
                     'found end of bad numbers
                     'set the RTB selection length to grab this range
                     Dim endbad As Int32 = RichTextBox1.GetFirstCharIndexFromLine(i)
                     RichTextBox1.SelectionLength = (endbad - RichTextBox1.SelectionStart)
                     Exit For 'stop scanning lines
                  Else
                     'keep processing lines
                     lastNumber = currentNumber
                     'store N#### location
                     NumberIndices.Add(New LineNumberRef(lastNumber, i))
                  End If 'ErrorExists

               End If 'lastNumber >= currentNumber 

            End If 'currentNumber <> -1 
         End If 'lines(i).StartsWith("N")
      Next i

      If ErrorExists And RichTextBox1.SelectionLength = 0 Then
         RichTextBox1.SelectionLength = (RichTextBox1.Text.Length - RichTextBox1.SelectionStart) + 1
      End If
      RichTextBox1.ScrollToCaret()
      Return ErrorExists
   End Function

   Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
      Button1.Enabled = False 'prevent 2ndry execution until this finishes
      'the boolean function ValidateN_Numbers will search for a block of code in the CNC program
      'in which the N####'s are numerically less than that of the preceeding lines
      'and return true if an invalid order is found.
      If Not ValidateN_Numbers() Then MsgBox("Validates")
      RichTextBox1.Focus() 'focus is currently on the button, send back to the RTB
      Button1.Enabled = True 'code is complete, allow the user to run it again
   End Sub

   Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
      Button2.Enabled = False 'prevent 2ndry execution until this finishes
      'OrderOnN_Numbers will resequence the CNC program lines base on N#### order
      'See the method for more info
      OrderOnN_Numbers()
      RichTextBox1.SelectionStart = 0 'Move the RTB to the beginning of the file
      RichTextBox1.Focus() 'focus is currently on the button, send back to the RTB
      Button2.Enabled = True 'code is complete, allow the user to run it again
   End Sub
End Class

I did not realize that it was as requirement of this site to post fully documented code

It is not a requirement to post fully documented code but my feeling is that because we usually do not know the level of expertise of the OP it is better to add a few comments to make it easier to follow the logic of the code. Please note my use of the word "some".

a simple sort was probably not a good idea as it could easily make a bad situation worse

You are correct to have pointed this out. And let me say again, your efforts are appreciated.

since this program worked so well I figured I ask: If I wanted to simply renumber the "N####" thorughout the file, starting at N010 and incrementing by 10 each time, how would I do this? I would want to simply copy and paste the entire file (even the lines that do not start with "N") and just append the numbers. Thank you for the help thus far!

For Each linenumb As Integer In sd.Keys
ListBox1.Items.Add(linenumb.ToString("0000") & sd(linenumb))
Next

If sd is the SortedDictionary in my first response then

Dim newline As Integer = 0

For Each oldline As Integer In sd.Keys
    newline += 10
    ListBox1.Items.Add("N" & newline.ToString("0000") & sd(oldline))
Next

will add the code to ListBox1 with line numbers ranging from N0010 in increments of 10. Warning - if the old line numbers are used as the target of a GOTO then the GOTOs will point to the wrong lines after the resequencing.

I see what you mean, I was really thinking of a process different than the initial sort (maybe I should start a new thread). The function of the program would be to directly copy the entire CNC file and that only thing it would edit is the incrementing numbers for example (the first code is the original, second would be the output):

****INPUT*******

; 
; ====== PROGRAM JUMP IN ======
IF LAST_TOOL==8002
  T8002
  GOTOF T_8002
ENDIF
; =============================
; 
%_N_OP080_01_MPF
;$PATH=/_N_WKS_DIR/_N_OP080_WPD
;###########################################################
;###########################################################
; 
N1000 G71
N1010 $AC_TIMER[1]=0         ;TOTAL CYCLE-TIME + LOADING AND UNLOADING
N1020 $AC_TIMER[2]=0         ;SINGLE TIME EACH TOOL
N1030 $AC_TIMER[3]=0         ;TOTAL CYCLE-TIME
;
N1130 $AC_TIMER[2]=-1
N1140 R70=R70+1
N1150 R[R70]=$AC_TIMER[2]
N1160 $AC_TIMER[2]=0
; 
; *** insert machine startup code here ***
N100 
; 
;===================== PROGRAM JUMP IN =====================
N1040 IF (LAST_TOOL <> 0)
N1050   WHEN TRUE DO $R21=1
N1060   T1=LAST_TOOL
N1070   GOTOF (<<"T_"<<LAST_TOOL)
N1080 ENDIF
N1090 WHEN TRUE DO $R21=0
;===========================================================
; 
N1100 T_801:
N1110 M21 M11
N1120 G0 G508 X0.0 Y0.0 A=DC(-90.0) B=DC(0.0) M20 M10
; 
;###########################################################

*******OUTPUT*******

; 
; ====== PROGRAM JUMP IN ======
IF LAST_TOOL==8002
  T8002
  GOTOF T_8002
ENDIF
; =============================
; 
%_N_OP080_01_MPF
;$PATH=/_N_WKS_DIR/_N_OP080_WPD
;###########################################################
;###########################################################
; 
N0010 G71
N0020 $AC_TIMER[1]=0         ;TOTAL CYCLE-TIME + LOADING AND UNLOADING
N0030 $AC_TIMER[2]=0         ;SINGLE TIME EACH TOOL
N0040 $AC_TIMER[3]=0         ;TOTAL CYCLE-TIME
;
N0050 $AC_TIMER[2]=-1
N0060 R70=R70+1
N0070 R[R70]=$AC_TIMER[2]
N0080 $AC_TIMER[2]=0
; 
; *** insert machine startup code here ***
N0090 
; 
;===================== PROGRAM JUMP IN =====================
N0100 IF (LAST_TOOL <> 0)
N0110   WHEN TRUE DO $R21=1
N0120   T1=LAST_TOOL
N0130   GOTOF (<<"T_"<<LAST_TOOL)
N0140 ENDIF
N0150 WHEN TRUE DO $R21=0
;===========================================================
; 
N0160 T_801:
N0170 M21 M11
N0180 G0 G508 X0.0 Y0.0 A=DC(-90.0) B=DC(0.0) M20 M10
; 
;###########################################################

So now it doesn't matter that the lines of code are out of sequence?

Resequencing the line numbers is fairly trivial. The issue is if there are any GOTO’s in the CNC code. Based on a quick internet search, a GOTO can be defined line number. The line number can be either directly specified or via a variable. Variables are prefixed with the hash “#” symbol followed by an integer.
For example:

N100
GOTO 100

100=100

GOTO #100

To remap the GOTO statements, I used a dictionary (of int32, int32) where the key value is the N####’s original numeric value. It allows looking up the original value and retrieving the new line number. In the code that follows, only variables used in a GOTO statement will be treated as line numbers and be remapped to the new line number. To test this logic, I added these lines to the end of your example.

    #100=1000
    #101=1000
    #101=1040
    GOTO #101
    GOTO 100

There appears to be other types of GOTO's used in CNC code (GOTOF, GOTOB), if you need to account for these, you can follow the pattern demonstrated in my code.

Here is the code. It writes the original code followed by the resequenced code to a RichTextBox.

Public Class Form1

   Private Const fmt As String = "0000"

   'a class to assist in storing info about lines that define a variable
   'variable start with a '#' followed by an integer
   Private Class VariableLine
      Public Name As Int32
      Public LineNumber As Int32 'the line of code that contains the declaration
      Public IndexofLineNumber As Int32 'in line index of the number assigned to the variable
      Public Sub New(ByVal name As Int32, ByVal LineNumber As Int32, ByVal IndexofLineNumber As Int32)
         Me.Name = name
         Me.LineNumber = LineNumber
         Me.IndexofLineNumber = IndexofLineNumber
      End Sub
   End Class

   Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
      Dim NewNumber As Int32 = 0 'The new line number
      Dim Increment As Int32 = 10 'Increment each line by this value

      Dim lines As New List(Of String) 'a list to hold each line of code

      'a dictionary of old line numbers and the corresponding new line number
      'use to remap GOTO's and Lines that set a variable to a line number
      'the variable remapping may cause an issue if the variable is not used as a line number
      Dim Mapped As New Dictionary(Of Int32, Int32)

      Dim LinesWithGoto As New List(Of Int32)
      Dim VariablesUsedInGotos As New List(Of Int32)
      Dim LineswithVariableDeclarations As New List(Of VariableLine)

      Dim fs As New IO.FileStream("cnc_code.txt", IO.FileMode.Open)
      Dim sr As New IO.StreamReader(fs)


      Dim line As String
      Dim index As Int32
      Dim currentnumber As Int32
      Dim RemainderOfLine As String

      RichTextBox1.Clear()
      RichTextBox1.AppendText("Original code" & vbLf & "--------------" & vbLf)
      Do Until sr.EndOfStream
         line = sr.ReadLine 'read line from file
         RichTextBox1.AppendText(line & vbLf)
         'Process line
         line = line.Trim 'eliminate trailing and leading spaces
         If line.StartsWith("N") Then 'found a N####?
            index = line.IndexOf(" ", 1) 'find end of N####
            currentnumber = -1 'initialize potential N#### to invalid value
            RemainderOfLine = ""
            If index = -1 Then 'no end found, may be just N### on line
               Int32.TryParse(line.Substring(1), currentnumber)
            Else
               RemainderOfLine = line.Substring(index)
               Int32.TryParse(line.Substring(1, index - 1), currentnumber)
            End If

            If currentnumber <> -1 Then 'Parsing succeeded, have N####
               NewNumber += Increment
               Mapped.Add(currentnumber, NewNumber)
               line = "N" & NewNumber.ToString(fmt) & RemainderOfLine
            End If
         End If 'lines(i).StartsWith("N")
         lines.Add(line)

      'check if a variable is defined. if so, store its location
         If line.Length > 0 AndAlso line(0) = "#"c Then 'Variable Declaration
            Dim Indexofequals As Int32 = line.IndexOf("="c)
            Dim variablename As String = line.Substring(1, Indexofequals - 1).Trim
            Dim number As Int32 = -1
            Int32.TryParse(line.Substring(Indexofequals + 1), number)
            If number <> -1 Then
               LineswithVariableDeclarations.Add(New VariableLine(CInt(variablename), lines.Count - 1, Indexofequals + 1))
            End If
           Continue Do
         End If
      'scan line for GOTO, do not scan comments (starts with ";")
         If line.Length > 0 AndAlso line(0) <> ";"c Then
            index = line.IndexOf("GOTO ")
            If index <> -1 Then
               LinesWithGoto.Add(lines.Count - 1)
               RemainderOfLine = line.Substring(index + 4).Trim
               If RemainderOfLine.Length > 0 AndAlso RemainderOfLine(0) = "#"c Then
                  Dim var As Int32 = -1
                  Int32.TryParse(RemainderOfLine.Substring(1), var)
                  If var <> -1 AndAlso Not VariablesUsedInGotos.Contains(var) Then
                     VariablesUsedInGotos.Add(var)
                  End If
               End If

            End If
         End If

      Loop
      sr.Close()

   'Process Lines with GOTO in them
      For Each i As Int32 In LinesWithGoto
         index = lines(i).IndexOf("GOTO ")
         RemainderOfLine = lines(i).Substring(index + 5).Trim
         If RemainderOfLine(0) = "#" Then 'GOTO variable
            'leave it alone
         Else 'GOTO N##### without "N"
            Dim oldnumber As Int32 = -1

            Int32.TryParse(RemainderOfLine, oldnumber)
            If oldnumber <> -1 Then
               lines(i) = lines(i).Substring(0, index + 5) & _
                          Mapped(oldnumber).ToString(fmt)
            End If
         End If

      Next i

   'Process Lines where variable is set
      For Each vl As VariableLine In LineswithVariableDeclarations
         If VariablesUsedInGotos.Contains(vl.Name) Then
            'rewrite the variable declaration line
            Dim oldnumber As Int32 = -1 'the old line # to lookup in Mapped
            Int32.TryParse(lines(vl.LineNumber).Substring(vl.IndexofLineNumber), oldnumber)
            lines(vl.LineNumber) = lines(vl.LineNumber).Substring(0, vl.IndexofLineNumber) & _
                                   Mapped(oldnumber).ToString(fmt)
         End If

      Next vl

   'I just display the modified lines in a RichTextBox
   'You can write them to a new file

      With RichTextBox1
         .AppendText("Modified code" & vbLf & "--------------" & vbLf)

         .WordWrap = False
         .ScrollBars = RichTextBoxScrollBars.ForcedBoth
         For i As Int32 = 0 To lines.Count - 1 'load RTB with CNC program
            .AppendText(lines(i) & vbLf)
         Next
      End With
   End Sub
End Class

@Reverend Jim, basically yes, this would be for a program that was "known" to be correct but after many edits the lines might be numbered N0001, N0002, N0003, N0010, and this just renumber the lines, I can see how to split the lines and simply renumber the "N" lines, but I want to keep all the line in-tact without losing any of the format (commented lines and such).

I think TnTinMN, you might be fixated on the GOTO, I do not believe those actually call a specific line number just a specific tool for the CNC driver (indexing a tool, re-locating to zero point hole), Keeping all the lines in order wouldn't have any effect on the GOTO.

@TnTinMN, it is breathtaking the amount of code you can whip up on a whim, you must be a true connoisseur

Keeping all the lines in order wouldn't have any effect on the GOTO

In that case I think you can get away with

       Dim newline As Integer = 0

        For Each line As String In System.IO.File.ReadAllLines("D:\temp\jumpin.txt")

            ListBox1.Items.Add(line)

            Dim linenumb As String 'the N#### portion of the line
            Dim linecode As String 'everything other than N####

            If line.StartsWith("N") Then
                newline += 10
                linenumb = Split(line)(0).Substring(1)
                linecode = line.Substring(Len(linenumb) + 1)
                line = "N" & newline.ToString("0000") & linecode
            End If

            ListBox2.Items.Add(line)

        Next
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.