'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'																																	'
' Multi-Z File-Open Macro/Script by theminor																						'
' Version 0.6																														'
'																																	'
' This Macro is designed to replace the standard File -> Open button in Mach, particularly for use with the Mach 2010 Screenset		'
' (although it can probably be used with other screensets). The macro works by presenting the standard File-Open dialog for the		'
' user to open a gcode file. Upon selecting a file, the user is prompted if he wants the file to be parsed (the prompt can be		'
' turned-off via the settings below so that the parsing happens automatically without prompting). If the file is to be parsed,		'
' the Script will close the file out of the Mach interface and re-open the file internally for parsing. The script will search		'
' for all toolchange operations which select a tool number set by the user in the settings below. If an applicable toolchange is	'
' found, all "Z" axis moves in that toolchange section will be modified to axis movements on the A, B, or C Axis (set by the		'
' user) for the user's Second Z axis. A "G52" offset command is also inserted in these sections to accont for the offset (set by	'
' user) of the Second Z axis from the First Z axis. Finally, M codes are modified in these sections to change the M codes that		'
' turn the spindle on and off in these sections to user-selectible codes for the second Z axis. The offset and all other			'
' modifications are deleted/removed in sections that do not fall under toolchanges for tha applicable tool. Once the file is		'
' parsed, the file is saved under a new file name with a prefix set by the user. The modified file is then opened in Mach, ready	'
' for execution as with any other gcode file.																						'
'																																	'
' This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. Please see:				'
' http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US																		'
' This software is provided "as-is," without any express or implied warranty. In no event shall the author be held liable for any	'
' damages arising from the use of this software. This software may not be redistributed in any way in it's original or altered form.'
'																																	'
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Global rtnArray() As String						' global Array used due to the weirdness that Functions can't return arrays

Sub Main()
	Dim z2SafeZ, z1SafeZ, insertFirstGcodeLine, modFileNamePrefix, toolNumToParse, modificationComment, xG52Offset, yG52Offset, z2AxisLetter, z1MCodeOn, z2MCodeOn, z1MCodeOff, z2MCodeOff As String
	Dim promptBeforeParsing, addMoveToSafeZPriorToToolchange, resetG52AtEndOfFile, changeToTool1AtEndOfFile, goToSafeZPAtEndOfFile, addSpindlesOffCommandsAtEndOfFile As Boolean
	
	'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
	' Settings:																		'
	' These can be changed by modifying the variable assignemtns in this section	'
	'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
	toolNumToParse = "2"						' the Tool Number we want to monitor for. "TX" commands (where "X" is the tool number) will be parsed such that all Z movements thereafter (until a subsequent "T" call) will be converted to the new axis
	z2AxisLetter = "A"							' the single-letter axis designation that is for the second Z axis - all Z applible Z moves will be changed to this letter within the tool nidicated above.
	xG52Offset = "4.2924"						' the X offset of our second Z axis from the first Z axis (in string format)
	yG52Offset = "-15.0416"						' the X offset of our second Z axis from the first Z axis (in string format)
	z1MCodeOn = "3"								' the M-Code (less the "M") used to turn the spindle on (to replace with a user defined M code in applicable sections)
	z2MCodeOn = "15"							' the M-Code that the M code above will be converted to in applicable sections
	z1MCodeOff = "5"							' the M-Code (less the "M") used to turn the spindle off (to replace with a user defined M code in applicable sections)
	z2MCodeOff = "16"							' the M-Code that the M code above will be converted to in applicable sections
	modFileNamePrefix = "Mod-"					' prefix to add to the file name to save the modified g-code to
	z1SafeZ = "1.0"								' Safe Z location for first Z axis
	z2SafeZ = "0.8"								' Safe Z location for second Z axis
	promptBeforeParsing = true					' set to true to prompt the user prior to parsing the file. If false, it will parse immediatly without prompting
	addMoveToSafeZPriorToToolchange = true		' Set to true if you want an instruction added just before a toolchange which will move both Z axes to their Safe Z locations set in the setting above
	resetG52AtEndOfFile = true					' Set to true if you want to add a command at the end of the file to reset the G52 offset back to 0,0. Note this will only apply if the end of the file comes while the applicable toolchange tool # is active.
	changeToTool1AtEndOfFile = true				' Set to true if you want to add a command at the end of the file to cange the tool back to Tool #1. Note this will only apply if the end of the file comes while the applicable toolchange tool # is active.
	goToSafeZPAtEndOfFile = true				' Set to true if you want to add a command at the end of the file to move both spindles back to the Safe Z positions set above. Note this will only apply if the end of the file comes while the applicable toolchange tool # is active.
	addSpindlesOffCommandsAtEndOfFile = true	' Set to true if you want to add a command at the end of the file to turn off both the spindles (using the M Codes set above). Note this will only apply if the end of the file comes while the applicable toolchange tool # is active.
	modificationComment = " (*)"				' additional text (comment) to insert on changed gcode lines
	insertFirstGcodeLine = "(file has been parsed and modified to account for second Z axis movement on A axis)"		' set to insert a commnet in the top of the gcode file showing that it has been modified
	'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
	' End of Settings Section														'
	'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

	Dim pathAndFileName, modifiedFileName, modifiedFileNameAndPath, fileName, filePath, oldFileName, oldFilePath, NEWLINE As String
	Dim origGCodeLines() As String
	Dim parseFile As Boolean
	oldFileName = GetLoadedGCodeFileName()
	oldFilePath = GetLoadedGCodeDir()
	NEWLINE = Chr(10)
	parseFile = true
	DoOEMButton(216)																					' first we perform a normal load of the G-Code file into Mach, take the filename and path from it, then close it --- Button 216 = File -> Open
	While IsLoading()
		Sleep(100)																						' wait until we are sure file is loaded
	Wend
	fileName = GetLoadedGCodeFileName()
	filePath = GetLoadedGCodeDir()
	pathAndFileName = filePath & fileName
	modifiedFileName = modFileNamePrefix & fileName
	modifiedFileNameAndPath = filePath &  modifiedFileName
	If fileName <> "" And Not (fileName = oldFileName And filePath = oldFilePath) Then					' Only Act if A file has been loaded - i.e. the user did not hit cancel (either the filename is blank or matches the prior loaded file exactly).
		If promptBeforeParsing = true Then
			If MsgBox("Parse this G-Code File for Dual Z Axis Functionality?" & NEWLINE & "Click Yes to Parse; Click No to leave the file intact.", 36, "Parse G-Code?") = 6 Then			' In MsgBox(), Argument 2 being 36 gives Yes/No Buttons with a Question Mark Icon. Return Value of 6 means Yes was clicked
				parseFile = true
			Else
				parseFile = false
			End If
		End If
		If parseFile = true Then																		' Only parse the file if we have prompted with an "OK" or it is set to not prompt
			Dim endOfFileStuffAdded As Boolean
			endOfFileStuffAdded = false
			DoOEMButton(169)																			' Close File
			If pathAndFileName <> "" Then
				Dim counter As Long
				Dim lnStg, outLine As String
				Dim inZ2Section As Boolean
				Dim indexCounter As Integer
				counter = 0
				outLine = ""
				lnStg = ""
				inZ2Section = false
				indexCounter = 0
				Open pathAndFileName For Input As #1
				While Not EOF(1)																		' Now we need to load Gcode File into an array of strings, 1 for each line of the file
					Line Input #1, tmp																	' first we count the lines in the file and close it
					counter = counter + 1
				Wend																					' we now know the proper size of the array
				Close #1
				ReDim origGCodeLines(counter)															' Set up the array to be of proper size
				counter = 0
				lnStg = ""
				Open pathAndFileName For Input As #1
				Do While Not EOF(1)																		' Now parse the file again in order to fill the array
					Line Input #1, lnStg
					origGCodeLines(counter) = lnStg
					counter = counter + 1
				Loop																					' Next we close the file and then re-open it for writing
				Close #1
				Open modifiedFileNameAndPath For Output As #2
				If insertFirstGcodeLine <> "" Then														' if user has set a first line comment to show the file has been modified, write that now
					Print #2, insertFirstGcodeLine
				End If
				For Each inLine In origGCodeLines														' now begin parsing each gcode line stored in our array
					outLine = Trim(inLine)
					Dim lineWords() As String
					Dim appendG52Offset, prependEndOfFileStuff, prependTCLine As String
					appendG52Offset = ""
					prependTCLine = ""
					prependEndOfFileStuff = ""
					SplitAtSpaces(outLine) 																' seperate each word out of the line
					lineWords = rtnArray																' due to weirdness in Mach's VB implamentation, this function can't return an array so we use a global variable instead
					For Each word In lineWords
						Dim firstChar As String
						firstChar = UCase(Left(outLine, 1))
						If firstChar = "T" Then															' Look at the first character on the word - if it is a "T" code, we are interested
							If Mid(outLine, 2, 1) = toolNumToParse Or Mid(outLine, 2, 2) = toolNumToParse Or Mid(outLine, 2, 2) = "0" & toolNumToParse Then				' Now look at all characters after the T up to the next space and compare to our magic Tool Number set above
								inZ2Section = true														' we are in a magic "Z2" section, so set Z moves accordingly
								appendG52Offset = modificationComment & NEWLINE & "M" & z2MCodeOn & modificationComment & NEWLINE & "G52 X" & xG52Offset & " Y" & yG52Offset		' and add a line implementing our G52Offset
								prependTCLine = "M" & z1MCodeOff & modificationComment & NEWLINE
							Else
								inZ2Section = false														' we are no longer in a Z2 section, so remove modifications to Z movements
								appendG52Offset = modificationComment & NEWLINE & "M" & z1MCodeOn & modificationComment & NEWLINE & "G52 X0 Y0" & modificationComment				' and cancel out our G52 Offset
								prependTCLine = "M" & z2MCodeOff & modificationComment & NEWLINE
							End If
							If addMoveToSafeZPriorToToolchange = true Then
								prependTCLine = prependTCLine & "G0 Z" & z1SafeZ & " " & z2AxisLetter & z2SafeZ & modificationComment & NEWLINE
							End If
						End If
						If inZ2Section = true Then														' Assess to see if the word is for Z movement AND we are in a Z2 section right now
							outLine = Replace(outLine, "Z", z2AxisLetter)								' change all Z's on this line to the letter set by z2AxisLetter
							If Mid(word, 2) = z1MCodeOn Or Mid(word, 3) = z1MCodeOn Or Mid(word, 3) = "0" & z1MCodeOn Then		' Next look for M codes that turn the spindle on
								outLine = Replace(outLine, "M0" & z1MCodeOn, "M" & z2MCodeOn)
								outLine = Replace(outLine, "M" & z1MCodeOn, "M" & z2MCodeOn)			' Replace all such M codes with our new M code
							End If
							If Mid(word, 2) = z1MCodeOff Or Mid(word, 3) = z1MCodeOff Or Mid(word, 3) = "0" & z1MCodeOff Then
								outLine = Replace(outLine, "M0" & z1MCodeOff, "M" & z2MCodeOff)			' we do both of these to account for a trailing "0"
								outLine = Replace(outLine, "M" & z1MCodeOff, "M" & z2MCodeOff)			' Replace all such M codes with our new M code
							End If
							If Left(word, 3) = "M30" Or Left(word, 3) = "m30" Or word = "%" Then		' We are at the end of the file - add whatever is to be added according to the settings
								If endOfFileStuffAdded = false Then
									If goToSafeZPAtEndOfFile = true Then
										prependEndOfFileStuff = prependEndOfFileStuff & "G0 Z" & z1SafeZ & " " & z2AxisLetter & z2SafeZ & modificationComment & NEWLINE
									End If
									If addSpindlesOffCommandsAtEndOfFile = true Then
										prependEndOfFileStuff = prependEndOfFileStuff & "M" & z1MCodeOff & " M" & z2MCodeOff & modificationComment & NEWLINE
									End If
									If changeToTool1AtEndOfFile = true Then
										prependEndOfFileStuff = prependEndOfFileStuff & "T1 M06" & modificationComment & NEWLINE
									End If
									If resetG52AtEndOfFile = true Then
										prependEndOfFileStuff = prependEndOfFileStuff & "G52 X0 Y0" & modificationComment & NEWLINE
									End If
									endOfFileStuffAdded = true
								End If
							End If
						End If
					Next
					outLine = prependTCLine & prependEndOfFileStuff & outLine & appendG52Offset
					If inZ2Section = true And Trim(outLine) <> "" Then
						outLine = outLine & modificationComment
					End If
					Print #2, outLine																	' Finally, add the modified G-code Line onto the total G-Code generated so far.
				Next
				Close #2
				LoadFile(modifiedFileNameAndPath)														' When all lines are writte, close the file for writing and load the gcode into Mach
				While IsLoading()																		' wait until we are sure file is loaded
					Sleep(100)
				Wend
			End If
			SetUserLabel (2, modifiedFileName)															' Set the name of the loaded file into the DRO in the Mach 2010 screenset
		Else
			SetUserLabel (2, fileName)																	' this "else" gets called if we did no conversion/parsing of the G-Code. Merely set the label with the origional file, which was never closed
		End If
	End If
End Sub


Function SplitAtSpaces(stg)
	Dim spaceLocations() As Integer																		' Search stg for every occurance of delineator and return an array of strings split by such delineator
	Dim breakMe As Boolean
	Dim spaceCount, pos, stgLength As Integer
	breakMe = false
	spaceCount = 0
	pos = 0
	stgLength = Len(stg)
	While breakMe = false																				' first count the total spaces to determine the sizes of our arrays
		pos = InStr(pos + 1, stg, " ")
		If pos > 1 Then
			spaceCount = spaceCount + 1
		Else
			breakMe = true
		End If
	Wend

	ReDim rtnArray(spaceCount)																			' now that we know the sizes, create the arrays to be filled
	ReDim spaceLocations(spaceCount)
	Dim cntr As Integer
	breakMe = false
	pos = 0
	cntr = 0
	While breakMe = false																				' now make an array of the locations of the delineators
		pos = InStr(pos + 1, stg, " ")
		If pos > 1 Then
			spaceLocations(cntr) = pos
			cntr = cntr+ 1
		Else
			breakMe = true
		End If
	Wend
	If spaceCount = 0 Then
		rtnArray(0) = stg
	Else
		For i = 0 To spaceCount																			' finally, go through and fill the array with the words
			If i = 0 Then
				rtnArray(0) = Left(stg, spaceLocations(0) - 1)
			Else
				If i = spaceCount Then																	' if we are on the last value, it is added differently
					rtnArray(i) = Right(stg, stgLength - spaceLocations(i - 1))
				Else																					' not the last one, so add the substring normally
					rtnArray(i) = Mid(stg, spaceLocations(i - 1) + 1, spaceLocations(i) - spaceLocations(i - 1) - 1)
				End If
			End If
		Next
	End If																								' Apparently Mach's Scripting language can't return Arrays (I tried) so we just modify rtnArray() on the fly, which is a global variable to the script and gets accessed both in this function and where needed in Main()
End Function


Function Replace(stg, findStg, rplcStg)																	' in String stg, replace every occurance of findStg with rplcStg
	Dim pos As Integer
	Dim breakMe As Boolean
	breakMe = false
	pos = 0
	While breakMe = false
		pos = InStr(pos + 1, stg, findStg)
		If pos > 1 Then																					' substring found - replace it and do it again
			Dim lStg, rStg As String
			lStg = Left(stg, pos - 1)
			rStg = Right(stg, Len(stg) - pos - Len(findStg) + 1)
			stg = lStg & rplcStg & rStg
		Else																							' no substring found - break
			breakMe = true
		End If
	Wend
	Replace = stg																						' Return the resulting string
End Function          
 
