/// <summary> /// Parse the header of a G-code file /// </summary> /// <param name="reader">Stream reader</param> /// <param name="partialFileInfo">G-code file information</param> /// <returns>Asynchronous task</returns> private static async Task ParseHeader(StreamReader reader, ParsedFileInfo partialFileInfo) { // Every time CTS.Token is accessed a copy is generated. Hence we cache one until this method completes CancellationToken token = Program.CancelSource.Token; Code code = new Code(); bool inRelativeMode = false, lastLineHadInfo = false, enforcingAbsolutePosition = false; do { token.ThrowIfCancellationRequested(); // Read another line string line = await reader.ReadLineAsync(); if (line == null) { break; } // See what codes to deal with bool gotNewInfo = false; using (StringReader stringReader = new StringReader(line)) { while (Code.Parse(stringReader, code, ref enforcingAbsolutePosition)) { if (code.Type == CodeType.GCode && partialFileInfo.FirstLayerHeight == 0) { if (code.MajorNumber == 91) { // G91 code (relative positioning) inRelativeMode = true; gotNewInfo = true; } else if (inRelativeMode) { // G90 (absolute positioning) inRelativeMode = (code.MajorNumber != 90); gotNewInfo = true; } else if (code.MajorNumber == 0 || code.MajorNumber == 1) { // G0/G1 is a move, see if there is a Z parameter present CodeParameter zParam = code.Parameter('Z'); if (zParam != null) { float z = zParam; if (z <= Settings.MaxLayerHeight) { partialFileInfo.FirstLayerHeight = z; gotNewInfo = true; } } } } else if (!string.IsNullOrWhiteSpace(code.Comment)) { gotNewInfo |= partialFileInfo.LayerHeight == 0 && FindLayerHeight(line, ref partialFileInfo); gotNewInfo |= FindFilamentUsed(line, ref partialFileInfo); gotNewInfo |= string.IsNullOrEmpty(partialFileInfo.GeneratedBy) && FindGeneratedBy(line, ref partialFileInfo); gotNewInfo |= partialFileInfo.PrintTime == 0 && FindPrintTime(line, ref partialFileInfo); gotNewInfo |= partialFileInfo.SimulatedTime == 0 && FindSimulatedTime(line, ref partialFileInfo); } code.Reset(); } } // Is the file info complete? if (!gotNewInfo && !lastLineHadInfo && IsFileInfoComplete(partialFileInfo)) { break; } lastLineHadInfo = gotNewInfo; }while (reader.BaseStream.Position < Settings.FileInfoReadLimitHeader + Settings.FileInfoReadBufferSize); }
/// <summary> /// Parse the header of a G-code file /// </summary> /// <param name="reader">Stream reader</param> /// <param name="partialFileInfo">G-code file information</param> /// <returns>Asynchronous task</returns> private static async Task ParseHeader(StreamReader reader, ParsedFileInfo partialFileInfo) { Code code = new Code(); CodeParserBuffer codeParserBuffer = new CodeParserBuffer(Settings.FileBufferSize, true); bool inRelativeMode = false, lastCodeHadInfo = false, gotNewInfo = false; long fileReadLimit = Math.Min(Settings.FileInfoReadLimitHeader + Settings.FileBufferSize, reader.BaseStream.Length); while (codeParserBuffer.GetPosition(reader) < fileReadLimit) { Program.CancellationToken.ThrowIfCancellationRequested(); if (!await DuetAPI.Commands.Code.ParseAsync(reader, code, codeParserBuffer)) { continue; } if (code.Type == CodeType.GCode && partialFileInfo.FirstLayerHeight == 0) { if (code.MajorNumber == 91) { // G91 code (relative positioning) inRelativeMode = true; gotNewInfo = true; } else if (inRelativeMode) { // G90 (absolute positioning) inRelativeMode = (code.MajorNumber != 90); gotNewInfo = true; } else if (code.MajorNumber == 0 || code.MajorNumber == 1) { // G0/G1 is a move, see if there is a Z parameter present CodeParameter zParam = code.Parameter('Z'); if (zParam != null) { float z = zParam; if (z <= Settings.MaxLayerHeight) { partialFileInfo.FirstLayerHeight = z; gotNewInfo = true; } } } } else if (!string.IsNullOrWhiteSpace(code.Comment)) { gotNewInfo |= (partialFileInfo.LayerHeight == 0) && FindLayerHeight(code.Comment, ref partialFileInfo); gotNewInfo |= FindFilamentUsed(code.Comment, ref partialFileInfo); gotNewInfo |= string.IsNullOrEmpty(partialFileInfo.GeneratedBy) && FindGeneratedBy(code.Comment, ref partialFileInfo); gotNewInfo |= (partialFileInfo.PrintTime == null) && FindPrintTime(code.Comment, ref partialFileInfo); gotNewInfo |= (partialFileInfo.SimulatedTime == null) && FindSimulatedTime(code.Comment, ref partialFileInfo); } // Is the file info complete? if (!gotNewInfo && !lastCodeHadInfo && IsFileInfoComplete(partialFileInfo)) { break; } lastCodeHadInfo = gotNewInfo; code.Reset(); } }
/// <summary> /// Parse the footer of a G-code file /// </summary> /// <param name="reader">Stream reader</param> /// <param name="partialFileInfo">G-code file information</param> /// <returns>Asynchronous task</returns> private static async Task ParseFooter(StreamReader reader, ParsedFileInfo partialFileInfo) { CancellationToken token = Program.CancelSource.Token; reader.BaseStream.Seek(0, SeekOrigin.End); Code code = new Code(); bool inRelativeMode = false, lastLineHadInfo = false, hadFilament = partialFileInfo.Filament.Count > 0, enforcingAbsolutePosition = false; char[] buffer = new char[Settings.FileInfoReadBufferSize]; int bufferPointer = 0; do { token.ThrowIfCancellationRequested(); // Read another line ReadLineFromEndResult readResult = await ReadLineFromEndAsync(reader, buffer, bufferPointer); if (readResult == null) { break; } bufferPointer = readResult.BufferPointer; // See what codes to deal with bool gotNewInfo = false; using (StringReader stringReader = new StringReader(readResult.Line)) { while (Code.Parse(stringReader, code, ref enforcingAbsolutePosition)) { if (code.Type == CodeType.GCode && partialFileInfo.Height == 0) { if (code.MajorNumber == 90) { // G90 code (absolute positioning) implies we were in relative mode inRelativeMode = true; gotNewInfo = true; } else if (inRelativeMode) { // G91 code (relative positioning) implies we were in absolute mode inRelativeMode = (code.MajorNumber != 91); gotNewInfo = true; } else if (code.MajorNumber == 0 || code.MajorNumber == 1) { // G0/G1 is an absolute move, see if there is a Z parameter present CodeParameter zParam = code.Parameter('Z'); if (zParam != null && (code.Comment == null || !code.Comment.TrimStart().StartsWith("E"))) { gotNewInfo = true; partialFileInfo.Height = zParam; } } } else if (!string.IsNullOrWhiteSpace(code.Comment)) { gotNewInfo |= partialFileInfo.LayerHeight == 0 && FindLayerHeight(readResult.Line, ref partialFileInfo); gotNewInfo |= !hadFilament && FindFilamentUsed(readResult.Line, ref partialFileInfo); gotNewInfo |= string.IsNullOrEmpty(partialFileInfo.GeneratedBy) && FindGeneratedBy(readResult.Line, ref partialFileInfo); gotNewInfo |= partialFileInfo.PrintTime == 0 && FindPrintTime(readResult.Line, ref partialFileInfo); gotNewInfo |= partialFileInfo.SimulatedTime == 0 && FindSimulatedTime(readResult.Line, ref partialFileInfo); } code.Reset(); } } // Is the file info complete? if (!gotNewInfo && !lastLineHadInfo && IsFileInfoComplete(partialFileInfo)) { break; } lastLineHadInfo = gotNewInfo; }while (reader.BaseStream.Length - reader.BaseStream.Position < Settings.FileInfoReadLimitFooter + buffer.Length); }
/// <summary> /// Parse the file for thumbnails /// </summary> /// <param name="reader">Stream reader</param> /// <param name="partialFileInfo">G-code file information</param> /// <returns>Asynchronous task</returns> /// <remarks> /// This functionality should be moved to ParseHeader in the long term /// </remarks> private static async Task ParseThumbnails(StreamReader reader, ParsedFileInfo parsedFileInfo) { Code code = new Code(); CodeParserBuffer codeParserBuffer = new CodeParserBuffer(Settings.FileBufferSize, true); bool imageFound = false; int encodedLength = 0; StringBuilder encodedImage = new StringBuilder(); reader.BaseStream.Seek(0, SeekOrigin.Begin); ParsedThumbnail thumbnail = null; while (codeParserBuffer.GetPosition(reader) < reader.BaseStream.Length) { Program.CancellationToken.ThrowIfCancellationRequested(); if (!await DuetAPI.Commands.Code.ParseAsync(reader, code, codeParserBuffer)) { continue; } if (code.Type != CodeType.Comment) { return; } if (string.IsNullOrEmpty(code.Comment)) { code.Reset(); continue; } if (code.Comment.Contains("thumbnail begin", StringComparison.InvariantCultureIgnoreCase)) { //Exit if we find another start tag before ending the previous image if (imageFound) { return; } string[] thumbnailTokens = code.Comment.Trim().Split(' '); //Stop processing since the thumbnail may be corrupt. if (thumbnailTokens.Length != 4) { return; } string[] dimensions = thumbnailTokens[2].Split('x'); if (dimensions.Length != 2) { continue; } imageFound = true; thumbnail = new ParsedThumbnail { Width = int.Parse(dimensions[0]), Height = int.Parse(dimensions[1]) }; encodedLength = int.Parse(thumbnailTokens[3]); encodedImage.Clear(); code.Reset(); continue; } else if (code.Comment.Contains("thumbnail end", StringComparison.InvariantCultureIgnoreCase)) { if (encodedImage.Length == encodedLength) { thumbnail.EncodedImage = "data:image/png;base64," + encodedImage.ToString(); parsedFileInfo.Thumbnails.Add(thumbnail); } thumbnail = null; imageFound = false; } else if (imageFound) { encodedImage.Append(code.Comment.Trim()); } code.Reset(); } }