static DrillGCode ParseGCode(GerberLineReader lineReader, GerberImage image) { DrillFileStats stats = image.DrillStats; DrillGCode result = DrillGCode.Unknown; int length = 0; int value = lineReader.GetIntegerValue(ref length); if (length == 2) { switch (value) { case 0: stats.G00++; result = DrillGCode.Rout; break; case 1: stats.G01++; result = DrillGCode.LinearMove; break; case 2: stats.G02++; result = DrillGCode.ClockwiseMove; break; case 3: stats.G03++; result = DrillGCode.CounterClockwiseMove; break; case 5: image.DrillStats.G05++; result = DrillGCode.Drill; break; case 90: stats.G90++; result = DrillGCode.Absolute; break; case 91: stats.G91++; result = DrillGCode.Incrementle; break; case 93: stats.G93++; result = DrillGCode.ZeroSet; break; default: stats.GUnknown++; break; } } return(result); }
static int ParseTCode(GerberLineReader lineReader, string drillFileName, DrillState drillState, GerberImage image) { int drillNumber; double drillSize = 0.0; int length = 0; char nextCharacter; bool done = false; DrillFileStats stats = image.DrillStats; string line = String.Empty; string errorMessage = String.Empty; nextCharacter = lineReader.Read(); if (!Char.IsDigit(nextCharacter)) { if (nextCharacter == 'C') { lineReader.Position -= 2; line = lineReader.ReadLineToEnd(); if (line == "TCST") { errorMessage = "Tool change stop switch found."; stats.AddNewError(-1, errorMessage, GerberErrorType.GerberNote, lineReader.LineNumber, drillFileName); } return(-1); } } lineReader.Position--; drillNumber = lineReader.GetIntegerValue(ref length); // T00 is a tool unload command. if (drillNumber == 0) { return(drillNumber); } if (drillNumber < ToolMin && drillNumber >= ToolMax) { errorMessage = String.Format(CultureInfo.CurrentCulture, "Drill number out of bounds:{0}.\n", drillNumber); stats.AddNewError(-1, errorMessage, GerberErrorType.GerberError, lineReader.LineNumber, drillFileName); } drillState.CurrentTool = drillNumber; // Tool definition following tool number. if (lineReader.Position > 0) { while (!done) { nextCharacter = lineReader.Read(); switch (nextCharacter) { case 'C': drillSize = GetDoubleValue(lineReader, drillState.HeaderNumberFormat, GerberOmitZero.OmitZerosTrailing, drillState.DecimalPlaces); if (drillState.Unit == GerberUnit.Millimeter) { drillSize /= 25.4; } else if (drillSize >= 4.0) { drillSize /= 1000.0; } if (drillSize <= 0 || drillSize >= 10000) { errorMessage = "Unreasonable drill size found."; stats.AddNewError(-1, errorMessage, GerberErrorType.GerberError, lineReader.LineNumber, drillFileName); } else { // Allow a redefinition of a tool only if all parameters are the same. if (image.ApertureArray[drillNumber] != null) { if (image.ApertureArray[drillNumber].Parameters[0] != drillSize || image.ApertureArray[drillNumber].ApertureType != GerberApertureType.Circle || image.ApertureArray[drillNumber].ParameterCount != 1 || image.ApertureArray[drillNumber].Unit != GerberUnit.Inch) { errorMessage = String.Format(CultureInfo.CurrentCulture, "Found redefinition if drill {0}.\n", drillNumber); stats.AddNewError(-1, errorMessage, GerberErrorType.GerberError); } } else { image.ApertureArray[drillNumber] = new ApertureDefinition(); image.ApertureArray[drillNumber].Parameters[0] = drillSize; image.ApertureArray[drillNumber].ApertureType = GerberApertureType.Circle; image.ApertureArray[drillNumber].ParameterCount = 1; image.ApertureArray[drillNumber].Unit = GerberUnit.Inch; } } string drillUnit = (drillState.Unit == GerberUnit.Millimeter) ? "MM" : "INCH"; stats.AddToDrillList(drillNumber, (drillState.Unit == GerberUnit.Millimeter) ? drillSize * 25.4 : drillSize, drillUnit); break; case 'F': case 'S': lineReader.GetIntegerValue(ref length); break; default: lineReader.Position--; done = true; break; } } } return(drillNumber); }
static DrillMCode ParseMCode(GerberLineReader lineReader, DrillState drillState, GerberImage image) { DrillFileStats stats = image.DrillStats; DrillMCode result = DrillMCode.Unknown; int length = 0; string line = String.Empty; int value = lineReader.GetIntegerValue(ref length); switch (value) { case 0: stats.M00++; result = DrillMCode.End; break; case 1: stats.M01++; result = DrillMCode.EndPattern; break; case 18: stats.M18++; result = DrillMCode.TipCheck; break; case 25: stats.M25++; result = DrillMCode.BeginPattern; break; case 30: stats.M30++; result = DrillMCode.EndRewind; break; case 31: stats.M31++; result = DrillMCode.BeginPattern; break; case 45: stats.M45++; result = DrillMCode.LongMessage; break; case 47: stats.M47++; result = DrillMCode.Message; break; case 48: stats.M48++; result = DrillMCode.Header; break; case 71: stats.M71++; result = DrillMCode.Metric; break; case 72: stats.M72++; result = DrillMCode.Imperial; break; case 95: stats.M95++; result = DrillMCode.EndHeader; break; case 97: stats.M97++; result = DrillMCode.CannedText; break; case 98: stats.M98++; result = DrillMCode.CannedText; break; default: stats.MUnknown++; break; } return(result); }
static bool ParseDrillSegment(string drillFileName, GerberLineReader lineReader, GerberImage image, DrillState drillState) { bool foundEOF = false; DrillFileStats stats = image.DrillStats; GerberNet currentNet = image.GerberNetList[0]; currentNet.Level = image.LevelList[0]; currentNet.NetState = image.NetStateList[0]; bool done = false; string line; string[] command; string errorMessage; char nextCharacter; while (!lineReader.EndOfFile && !foundEOF) { nextCharacter = lineReader.Read(); switch (nextCharacter) { case ';': // Comment. line = lineReader.ReadLineToEnd(); break; case 'D': lineReader.Position--; line = lineReader.ReadLineToEnd(); if (line.Substring(0, 6) == "DETECT") { stats.Detect = line.Substring(6, (line.Length - 7)); } else { errorMessage = String.Format(CultureInfo.CurrentCulture, "Undefined header line: {0}.\n", line); stats.AddNewError(-1, errorMessage, GerberErrorType.GerberNote, lineReader.LineNumber, drillFileName); } break; case 'F': lineReader.Position--; line = lineReader.ReadLineToEnd(); if (line != "FMAT,2") { errorMessage = String.Format(CultureInfo.CurrentCulture, "Undefined header line: {0}.\n", line); stats.AddNewError(-1, errorMessage, GerberErrorType.GerberNote, lineReader.LineNumber, drillFileName); } break; case 'G': DrillGCode gCode = ParseGCode(lineReader, image); switch (gCode) { case DrillGCode.Rout: errorMessage = "Rout Mode not supported.\n"; stats.AddNewError(-1, errorMessage, GerberErrorType.GerberNote, lineReader.LineNumber, drillFileName); break; case DrillGCode.Drill: break; case DrillGCode.Slot: nextCharacter = lineReader.Read(); ParseCoordinate(lineReader, nextCharacter, image, drillState); currentNet.StopX = drillState.CurrentX; currentNet.StopY = drillState.CurrentY; if (drillState.Unit == GerberUnit.Millimeter) { currentNet.StopX /= 25.4; currentNet.StopY /= 25.4; } currentNet.ApertureState = GerberApertureState.On; break; case DrillGCode.Absolute: drillState.CoordinateMode = DrillCoordinateMode.Absolute; break; case DrillGCode.Incrementle: drillState.CoordinateMode = DrillCoordinateMode.Incremental; break; case DrillGCode.ZeroSet: nextCharacter = lineReader.Read(); ParseCoordinate(lineReader, nextCharacter, image, drillState); drillState.OriginX = drillState.CurrentX; drillState.OriginY = drillState.CurrentY; break; default: line = lineReader.ReadLineToEnd(); break; } break; case 'I': // Inch header. if (drillState.CurrentSection != DrillFileSection.Header) { break; } nextCharacter = lineReader.Read(); switch (nextCharacter) { // Inch case 'N': lineReader.Position -= 2; line = lineReader.ReadLineToEnd(); command = line.Split(','); if (command[0] == "INCH") { drillState.Unit = GerberUnit.Inch; } if (command.Length == 2) { if (command[1] == "TZ") { if (drillState.AutoDetect) { image.Format.OmitZeros = GerberOmitZero.OmitZerosLeading; drillState.HeaderNumberFormat = DrillNumberFormat.Format_00_0000; drillState.DecimalPlaces = 4; } } else if (command[1] == "LZ") { image.Format.OmitZeros = GerberOmitZero.OmitZerosTrailing; drillState.HeaderNumberFormat = DrillNumberFormat.Format_00_0000; drillState.DecimalPlaces = 4; } else { errorMessage = "Invalid zero suppression found after INCH.\n"; stats.AddNewError(-1, errorMessage, GerberErrorType.GerberWarning, lineReader.LineNumber, drillFileName); } } else { // No TZ/LZ specified, use defaults. if (drillState.AutoDetect) { image.Format.OmitZeros = GerberOmitZero.OmitZerosLeading; drillState.HeaderNumberFormat = DrillNumberFormat.Format_00_0000; drillState.DecimalPlaces = 4; } } break; case 'C': lineReader.Position -= 2; line = lineReader.ReadLineToEnd(); command = line.Split(','); if (command.Length == 2) { if (command[1] == "ON") { drillState.CoordinateMode = DrillCoordinateMode.Incremental; } else if (command[1] == "OFF") { drillState.CoordinateMode = DrillCoordinateMode.Absolute; } else { errorMessage = "Invalid coordinate data found.\n"; stats.AddNewError(-1, errorMessage, GerberErrorType.GerberWarning, lineReader.LineNumber, drillFileName); } } else { errorMessage = "Invalid data found.\n"; stats.AddNewError(-1, errorMessage, GerberErrorType.GerberWarning, lineReader.LineNumber, drillFileName); } break; } break; case 'M': // M code or Metric nextCharacter = lineReader.Read(); if (!Char.IsDigit(nextCharacter)) // Not a M## command. { // Should be a metric command in header. // METRIC is only acceptable within the header section. // The syntax is METRIC[,{TZ|LZ}][,{000.000|000.00|0000.00}] ?????? if (drillState.CurrentSection != DrillFileSection.Header) { break; } done = true; lineReader.Position -= 2; // Point back to the start on the line. line = lineReader.ReadLineToEnd(); command = line.Split(','); if (command[0] == "METRIC") { drillState.Unit = GerberUnit.Millimeter; } if (command.Length > 1) { if (command[1] == "TZ") { if (drillState.AutoDetect) { image.Format.OmitZeros = GerberOmitZero.OmitZerosLeading; } done = false; } else if (command[1] == "LZ") { if (drillState.AutoDetect) { image.Format.OmitZeros = GerberOmitZero.OmitZerosTrailing; } done = false; } else { errorMessage = "Invalid zero suppression found after METRIC.\n"; stats.AddNewError(-1, errorMessage, GerberErrorType.GerberWarning, lineReader.LineNumber, drillFileName); done = true; } // Number format may or may not be specified. if (!done && command.Length == 3) { if (drillState.AutoDetect) { drillState.HeaderNumberFormat = drillState.DataNumberFormat = DrillNumberFormat.Format_000_000; drillState.DecimalPlaces = 3; } if (command[2] == "0000.00") { drillState.DataNumberFormat = DrillNumberFormat.Format_0000_00; drillState.DecimalPlaces = 2; } else if (command[2] == "000.000") { drillState.DataNumberFormat = DrillNumberFormat.Format_000_000; drillState.DecimalPlaces = 3; } else if (command[2] == "000.00") { drillState.DataNumberFormat = DrillNumberFormat.Format_000_00; drillState.DecimalPlaces = 2; } else { errorMessage = "Invalid number format found after TZ/LZ.\n"; stats.AddNewError(-1, errorMessage, GerberErrorType.GerberWarning, lineReader.LineNumber, drillFileName); } } } else { // No TZ/LZ or number format specified, use defaults. if (drillState.AutoDetect) { image.Format.OmitZeros = GerberOmitZero.OmitZerosLeading; drillState.HeaderNumberFormat = DrillNumberFormat.Format_000_000; drillState.DecimalPlaces = 3; } } } else if (Char.IsDigit(nextCharacter)) { // Must be an M## code. lineReader.Position--; DrillMCode mCode = ParseMCode(lineReader, drillState, image); switch (mCode) { case DrillMCode.Header: drillState.CurrentSection = DrillFileSection.Header; break; case DrillMCode.EndHeader: drillState.CurrentSection = DrillFileSection.Data; break; case DrillMCode.Metric: if (drillState.Unit == GerberUnit.Unspecified && drillState.CurrentSection != DrillFileSection.Header) { errorMessage = "M71 code found with no METRIC specification in header.\n"; stats.AddNewError(-1, errorMessage, GerberErrorType.GerberError, lineReader.LineNumber, drillFileName); errorMessage = "Assuming all tool sizes are in millimeters.\n"; stats.AddNewError(-1, errorMessage, GerberErrorType.GerberWarning); for (int toolNumber = ToolMin; toolNumber < ToolMax; toolNumber++) { if (image.ApertureArray[toolNumber] != null) { double toolSize = image.ApertureArray[toolNumber].Parameters[0]; stats.ModifyDrillList(toolNumber, toolSize, "MM"); image.ApertureArray[toolNumber].Parameters[0] /= 25.4; } } } if (drillState.AutoDetect) { drillState.DataNumberFormat = drillState.BackupNumberFormat; drillState.Unit = GerberUnit.Millimeter; } break; case DrillMCode.Imperial: if (drillState.AutoDetect) { if (drillState.DataNumberFormat != DrillNumberFormat.Format_00_0000) { drillState.BackupNumberFormat = drillState.DataNumberFormat; // Save format definition for later. } drillState.DataNumberFormat = DrillNumberFormat.Format_00_0000; drillState.DecimalPlaces = 4; drillState.Unit = GerberUnit.Inch; } break; case DrillMCode.LongMessage: case DrillMCode.Message: case DrillMCode.CannedText: line = lineReader.ReadLineToEnd(); // message here. break; case DrillMCode.NotImplemented: case DrillMCode.EndPattern: case DrillMCode.TipCheck: break; case DrillMCode.End: line = lineReader.ReadLineToEnd(); break; case DrillMCode.EndRewind: // EOF. //done = true; foundEOF = true; break; default: stats.AddNewError(-1, "Undefined M code.", GerberErrorType.GerberError, lineReader.LineNumber, drillFileName); break; } } break; case 'R': if (drillState.CurrentSection == DrillFileSection.Header) { stats.AddNewError(-1, "R code not allowed in the header.", GerberErrorType.GerberError, lineReader.LineNumber, drillFileName); } else { double stepX = 0.0, stepY = 0.0; int length = 0; image.DrillStats.R++; double startX = drillState.CurrentX; double startY = drillState.CurrentY; int repeatcount = lineReader.GetIntegerValue(ref length); nextCharacter = lineReader.Read(); if (nextCharacter == 'X') { stepX = GetDoubleValue(lineReader, drillState.DataNumberFormat, image.Format.OmitZeros, drillState.DecimalPlaces); nextCharacter = lineReader.Read(); } if (nextCharacter == 'Y') { stepY = GetDoubleValue(lineReader, drillState.DataNumberFormat, image.Format.OmitZeros, drillState.DecimalPlaces); } else { lineReader.Position--; } for (int i = 1; i < repeatcount; i++) { drillState.CurrentX = startX + i * stepX; drillState.CurrentY = startY + i * stepY; currentNet = AddDrillHole(image, drillState, currentNet); } } break; case 'S': // Ignore spindle speed. lineReader.ReadLineToEnd(); break; case 'T': int tool = ParseTCode(lineReader, drillFileName, drillState, image); break; case 'X': case 'Y': // Hole coordinate found. Do some parsing. ParseCoordinate(lineReader, nextCharacter, image, drillState); // Add the new drill hole. currentNet = AddDrillHole(image, drillState, currentNet); break; case '%': drillState.CurrentSection = DrillFileSection.Data; break; // Ignore white space or null characters. case '\n': case '\r': case ' ': case '\t': case '\0': break; } } return(foundEOF); }
public static ApertureMacro ProcessApertureMacro(GerberLineReader reader) { const int MathOperationStackSize = 2; ApertureMacro apertureMacro = new ApertureMacro(); GerberInstruction instruction; GerberOpcodes[] mathOperations = new GerberOpcodes[MathOperationStackSize]; char characterRead; int primitive = 0; int mathOperationIndex = 0; int equate = 0; bool continueLoop = true; bool comma = false; // Just read an operator (one of '*+X/) bool isNegative = false; // Negative numbers succeeding ',' bool foundPrimitive = false; int length = 0; // Get macro name apertureMacro.Name = reader.GetStringValue('*'); characterRead = reader.Read(); // skip '*' // The first instruction in all programs will be NOP. instruction = new GerberInstruction(); instruction.Opcode = GerberOpcodes.NOP; apertureMacro.InstructionList.Add(instruction); while (continueLoop && !reader.EndOfFile) { length = 0; characterRead = reader.Read(); switch (characterRead) { case '$': if (foundPrimitive) { instruction = new GerberInstruction(); instruction.Opcode = GerberOpcodes.PushParameter; apertureMacro.InstructionList.Add(instruction); apertureMacro.NufPushes++; instruction.Data.IntValue = reader.GetIntegerValue(ref length); comma = false; } else { equate = reader.GetIntegerValue(ref length); } break; case '*': while (mathOperationIndex != 0) { instruction = new GerberInstruction(); instruction.Opcode = mathOperations[--mathOperationIndex]; apertureMacro.InstructionList.Add(instruction); } // Check is due to some gerber files has spurious empty lines (eg EagleCad) if (foundPrimitive) { instruction = new GerberInstruction(); if (equate > 0) { instruction.Opcode = GerberOpcodes.PopParameter; instruction.Data.IntValue = equate; } else { instruction.Opcode = GerberOpcodes.Primative; instruction.Data.IntValue = primitive; } apertureMacro.InstructionList.Add(instruction); equate = 0; primitive = 0; foundPrimitive = false; } break; case '=': if (equate > 0) { foundPrimitive = true; } break; case ',': if (!foundPrimitive) { foundPrimitive = true; break; } while (mathOperationIndex != 0) { instruction = new GerberInstruction(); instruction.Opcode = mathOperations[--mathOperationIndex]; apertureMacro.InstructionList.Add(instruction); } comma = true; break; case '+': while ((mathOperationIndex != 0) && OperatorPrecedence((mathOperationIndex > 0) ? mathOperations[mathOperationIndex - 1] : GerberOpcodes.NOP) >= (OperatorPrecedence(GerberOpcodes.Add))) { instruction = new GerberInstruction(); instruction.Opcode = mathOperations[--mathOperationIndex]; apertureMacro.InstructionList.Add(instruction); } mathOperations[mathOperationIndex++] = GerberOpcodes.Add; comma = true; break; case '-': if (comma) { isNegative = true; comma = false; break; } while ((mathOperationIndex != 0) && OperatorPrecedence((mathOperationIndex > 0) ? mathOperations[mathOperationIndex - 1] : GerberOpcodes.NOP) >= (OperatorPrecedence(GerberOpcodes.Subtract))) { instruction = new GerberInstruction(); instruction.Opcode = mathOperations[--mathOperationIndex]; apertureMacro.InstructionList.Add(instruction); } mathOperations[mathOperationIndex++] = GerberOpcodes.Subtract; break; case '/': while ((mathOperationIndex != 0) && OperatorPrecedence((mathOperationIndex > 0) ? mathOperations[mathOperationIndex - 1] : GerberOpcodes.NOP) >= (OperatorPrecedence(GerberOpcodes.Divide))) { instruction = new GerberInstruction(); instruction.Opcode = mathOperations[--mathOperationIndex]; apertureMacro.InstructionList.Add(instruction); } mathOperations[mathOperationIndex++] = GerberOpcodes.Divide; comma = true; break; case 'X': case 'x': while ((mathOperationIndex != 0) && OperatorPrecedence((mathOperationIndex > 0) ? mathOperations[mathOperationIndex - 1] : GerberOpcodes.NOP) >= (OperatorPrecedence(GerberOpcodes.Multiple))) { instruction = new GerberInstruction(); instruction.Opcode = mathOperations[--mathOperationIndex]; apertureMacro.InstructionList.Add(instruction); } mathOperations[mathOperationIndex++] = GerberOpcodes.Multiple; comma = true; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '.': // Comments in aperture macros are a definition starting with zero and ends with a '*' if ((characterRead == '0') && (!foundPrimitive) && (primitive == 0)) { // Comment continues until next '*', just throw it away. reader.GetStringValue('*'); characterRead = reader.Read(); // Read the '*'. break; } // First number in an aperture macro describes the primitive as a numerical value if (!foundPrimitive) { primitive = (primitive * 10) + (characterRead - '0'); break; } reader.Position--; instruction = new GerberInstruction(); instruction.Opcode = GerberOpcodes.Push; apertureMacro.InstructionList.Add(instruction); apertureMacro.NufPushes++; instruction.Data.DoubleValue = reader.GetDoubleValue(); if (isNegative) { instruction.Data.DoubleValue = -(instruction.Data.DoubleValue); } isNegative = false; comma = false; break; case '%': reader.Position--; // Must return with % first in string since the main parser needs it. continueLoop = false; // Reached the end of the macro. break; default: // Whitespace. break; } } if (reader.EndOfFile) { return(null); } return(apertureMacro); }