2017-06-21 23:26:20 -07:00
|
|
|
|
/*
|
|
|
|
|
|
Copyright (c) 2016, Lars Brubaker
|
|
|
|
|
|
All rights reserved.
|
|
|
|
|
|
|
|
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
|
|
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
|
|
|
|
|
|
|
|
1. Redistributions of source code must retain the above copyright notice, this
|
|
|
|
|
|
list of conditions and the following disclaimer.
|
|
|
|
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
|
|
this list of conditions and the following disclaimer in the documentation
|
|
|
|
|
|
and/or other materials provided with the distribution.
|
|
|
|
|
|
|
|
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
|
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
|
|
|
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
|
|
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
|
|
|
|
The views and conclusions contained in the software and documentation are those
|
|
|
|
|
|
of the authors and should not be interpreted as representing official policies,
|
|
|
|
|
|
either expressed or implied, of the FreeBSD Project.
|
|
|
|
|
|
*/
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.IO;
|
2019-03-27 16:16:16 -07:00
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text.RegularExpressions;
|
2017-07-14 13:55:02 -07:00
|
|
|
|
using System.Threading;
|
2018-01-05 12:44:57 -08:00
|
|
|
|
using MatterHackers.Agg;
|
|
|
|
|
|
using MatterHackers.VectorMath;
|
2017-06-21 23:26:20 -07:00
|
|
|
|
|
2018-01-05 12:44:57 -08:00
|
|
|
|
namespace MatterControl.Printing
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2018-01-05 12:44:57 -08:00
|
|
|
|
public abstract class GCodeFile
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2019-03-27 16:16:16 -07:00
|
|
|
|
public const string PostProcessedExtension = ".postprocessed.gcode";
|
2018-05-26 12:16:23 -07:00
|
|
|
|
|
2017-06-21 23:26:20 -07:00
|
|
|
|
#if __ANDROID__
|
|
|
|
|
|
protected const int Max32BitFileSize = 10000000; // 10 megs
|
|
|
|
|
|
#else
|
|
|
|
|
|
protected const int Max32BitFileSize = 100000000; // 100 megs
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
public static void AssertDebugNotDefined()
|
|
|
|
|
|
{
|
|
|
|
|
|
#if DEBUG
|
|
|
|
|
|
throw new Exception("DEBUG is defined and should not be!");
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// the number of lines in the file
|
|
|
|
|
|
public abstract int LineCount { get; }
|
|
|
|
|
|
|
2017-06-29 13:35:44 -07:00
|
|
|
|
public abstract int LayerCount { get; }
|
2019-05-03 12:29:51 -07:00
|
|
|
|
|
2017-06-21 23:26:20 -07:00
|
|
|
|
public abstract double TotalSecondsInPrint { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public abstract void Clear();
|
|
|
|
|
|
|
|
|
|
|
|
public abstract RectangleDouble GetBounds();
|
|
|
|
|
|
|
|
|
|
|
|
public abstract double GetFilamentCubicMm(double filamentDiameter);
|
|
|
|
|
|
|
|
|
|
|
|
public abstract double GetFilamentDiameter();
|
|
|
|
|
|
|
|
|
|
|
|
public abstract double GetFilamentUsedMm(double filamentDiameter);
|
|
|
|
|
|
|
|
|
|
|
|
public abstract double GetFilamentWeightGrams(double filamentDiameterMm, double density);
|
|
|
|
|
|
|
2018-05-30 16:42:17 -07:00
|
|
|
|
public abstract int GetFirstLayerInstruction(int layerIndex);
|
2017-06-21 23:26:20 -07:00
|
|
|
|
|
2018-05-08 15:19:06 -07:00
|
|
|
|
public abstract double GetLayerHeight(int layerIndex);
|
|
|
|
|
|
|
2022-05-03 10:19:19 -07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Get the speed of the fan at the conculsion of this layer
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="layerIndex">The layer to get the last fan speed for</param>
|
|
|
|
|
|
/// <returns>The fan speed 0 to 255</returns>
|
|
|
|
|
|
public abstract int GetLastFanSpeed(int layerIndex);
|
|
|
|
|
|
|
2018-05-09 15:27:31 -07:00
|
|
|
|
public abstract double GetLayerTop(int layerIndex);
|
2017-06-21 23:26:20 -07:00
|
|
|
|
|
|
|
|
|
|
public abstract int GetLayerIndex(int instructionIndex);
|
|
|
|
|
|
|
|
|
|
|
|
public abstract Vector2 GetWeightedCenter();
|
|
|
|
|
|
|
|
|
|
|
|
public abstract PrinterMachineInstruction Instruction(int i);
|
|
|
|
|
|
|
|
|
|
|
|
public abstract bool IsExtruding(int instructionIndexToCheck);
|
2019-05-03 12:29:51 -07:00
|
|
|
|
|
2017-06-21 23:26:20 -07:00
|
|
|
|
public abstract double PercentComplete(int instructionIndex);
|
|
|
|
|
|
|
2019-05-19 20:10:28 -07:00
|
|
|
|
public abstract double Ratio0to1IntoContainedLayerSeconds(int instructionIndex);
|
|
|
|
|
|
|
|
|
|
|
|
public abstract double Ratio0to1IntoContainedLayerInstruction(int instructionIndex);
|
2017-06-21 23:26:20 -07:00
|
|
|
|
|
|
|
|
|
|
public static int CalculateChecksum(string commandToGetChecksumFor)
|
|
|
|
|
|
{
|
|
|
|
|
|
int checksum = 0;
|
|
|
|
|
|
if (commandToGetChecksumFor.Length > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
checksum = commandToGetChecksumFor[0];
|
|
|
|
|
|
for (int i = 1; i < commandToGetChecksumFor.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
checksum ^= commandToGetChecksumFor[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2019-05-03 12:29:51 -07:00
|
|
|
|
|
2017-06-21 23:26:20 -07:00
|
|
|
|
return checksum;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-03 12:29:51 -07:00
|
|
|
|
private static readonly Regex FirstDigitsAfterToken = new Regex("\\d+", RegexOptions.CultureInvariant | RegexOptions.Compiled);
|
2019-03-27 16:16:16 -07:00
|
|
|
|
|
2019-05-03 12:29:51 -07:00
|
|
|
|
private static readonly string[] LayerLineStartTokens = new[]
|
2019-03-27 16:16:16 -07:00
|
|
|
|
{
|
|
|
|
|
|
"; LAYER:",
|
|
|
|
|
|
";LAYER:",
|
|
|
|
|
|
"; layer ",
|
2021-09-08 16:32:23 -07:00
|
|
|
|
";LAYER_CHANGE" // prusa slicer default
|
2019-03-27 16:16:16 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
public static bool IsLayerChange(string line)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2021-09-08 16:32:23 -07:00
|
|
|
|
return LayerChangeString(line) != null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static string LayerChangeString(string line)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (int i = 0; i < LayerLineStartTokens.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (line.StartsWith(LayerLineStartTokens[i]))
|
|
|
|
|
|
{
|
|
|
|
|
|
return LayerLineStartTokens[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
2019-03-27 16:16:16 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static int GetLayerNumber(string line)
|
|
|
|
|
|
{
|
2021-09-08 16:32:23 -07:00
|
|
|
|
var layerChangeString = LayerChangeString(line);
|
2019-03-27 16:16:16 -07:00
|
|
|
|
|
2021-09-08 16:32:23 -07:00
|
|
|
|
if (layerChangeString != null)
|
2019-03-27 16:16:16 -07:00
|
|
|
|
{
|
2021-09-08 16:32:23 -07:00
|
|
|
|
line = line.Substring(layerChangeString.Length);
|
2019-03-27 16:16:16 -07:00
|
|
|
|
|
|
|
|
|
|
// Find the first digits after the layer start token
|
2019-05-03 12:29:51 -07:00
|
|
|
|
var match = FirstDigitsAfterToken.Match(line);
|
2019-03-27 16:16:16 -07:00
|
|
|
|
|
|
|
|
|
|
if (match.Success
|
|
|
|
|
|
&& int.TryParse(match.Value, out int layerNumber))
|
|
|
|
|
|
{
|
|
|
|
|
|
return layerNumber;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return 0;
|
2017-06-21 23:26:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-03-23 18:37:53 -07:00
|
|
|
|
public static bool FileTooBigToLoad(Stream fileStream)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2019-03-23 18:37:53 -07:00
|
|
|
|
if (Is32Bit)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
|
|
|
|
|
// Let's make sure we can load a file this big
|
2019-03-23 18:37:53 -07:00
|
|
|
|
if (fileStream.Length > Max32BitFileSize)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
|
|
|
|
|
// It is too big to load
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static bool GetFirstNumberAfter(string stringToCheckAfter, string stringWithNumber, ref int readValue, int startIndex = 0, string stopCheckingString = ";")
|
2019-03-25 14:23:45 -07:00
|
|
|
|
{
|
|
|
|
|
|
return GetFirstNumberAfter(stringToCheckAfter, stringWithNumber, ref readValue, out _, startIndex, stopCheckingString);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-03-25 17:55:52 -07:00
|
|
|
|
public static string GetLineWithoutChecksum(string inLine)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (inLine.StartsWith("N"))
|
|
|
|
|
|
{
|
|
|
|
|
|
int lineNumber = 0;
|
|
|
|
|
|
if (GCodeFile.GetFirstNumberAfter("N", inLine, ref lineNumber, out int numberEnd))
|
|
|
|
|
|
{
|
|
|
|
|
|
var outLine = inLine.Substring(numberEnd).Trim();
|
|
|
|
|
|
int checksumStart = outLine.IndexOf('*');
|
|
|
|
|
|
if (checksumStart != -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
return outLine.Substring(0, checksumStart);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return inLine;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-03-25 14:23:45 -07:00
|
|
|
|
public static bool GetFirstNumberAfter(string stringToCheckAfter, string stringWithNumber, ref int readValue, out int numberEnd, int startIndex = 0, string stopCheckingString = ";")
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
|
|
|
|
|
double doubleValue = readValue;
|
2019-05-03 12:29:51 -07:00
|
|
|
|
if (GetFirstNumberAfter(stringToCheckAfter, stringWithNumber, ref doubleValue, out numberEnd, startIndex, stopCheckingString))
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
|
|
|
|
|
readValue = (int)doubleValue;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static bool GetFirstNumberAfter(string stringToCheckAfter, string stringWithNumber, ref double readValue, int startIndex = 0, string stopCheckingString = ";")
|
2019-03-25 14:23:45 -07:00
|
|
|
|
{
|
|
|
|
|
|
return GetFirstNumberAfter(stringToCheckAfter, stringWithNumber, ref readValue, out _, startIndex, stopCheckingString);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static bool GetFirstNumberAfter(string stringToCheckAfter, string stringWithNumber, ref double readValue, out int numberEnd, int startIndex = 0, string stopCheckingString = ";")
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2018-06-12 11:11:28 -07:00
|
|
|
|
int stringPos = stringWithNumber.IndexOf(stringToCheckAfter, Math.Min(stringWithNumber.Length, startIndex));
|
2017-06-21 23:26:20 -07:00
|
|
|
|
int stopPos = stringWithNumber.IndexOf(stopCheckingString);
|
|
|
|
|
|
if (stringPos != -1
|
2018-05-08 15:19:06 -07:00
|
|
|
|
&& (stopPos == -1 || stringPos < stopPos || string.IsNullOrEmpty(stopCheckingString)))
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
|
|
|
|
|
stringPos += stringToCheckAfter.Length;
|
|
|
|
|
|
readValue = agg_basics.ParseDouble(stringWithNumber, ref stringPos, true);
|
2019-03-25 14:23:45 -07:00
|
|
|
|
numberEnd = stringPos;
|
2017-06-21 23:26:20 -07:00
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-03-25 14:23:45 -07:00
|
|
|
|
numberEnd = -1;
|
2017-06-21 23:26:20 -07:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static bool GetFirstStringAfter(string stringToCheckAfter, string fullStringToLookIn, string separatorString, ref string nextString, int startIndex = 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
int stringPos = fullStringToLookIn.IndexOf(stringToCheckAfter, startIndex);
|
|
|
|
|
|
if (stringPos != -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
int separatorPos = fullStringToLookIn.IndexOf(separatorString, stringPos);
|
|
|
|
|
|
if (separatorPos != -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
nextString = fullStringToLookIn.Substring(stringPos + stringToCheckAfter.Length, separatorPos - (stringPos + stringToCheckAfter.Length));
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-05-03 12:29:51 -07:00
|
|
|
|
public static GCodeFile Load(Stream fileStream,
|
2018-01-17 14:03:56 -08:00
|
|
|
|
Vector4 maxAccelerationMmPerS2,
|
|
|
|
|
|
Vector4 maxVelocityMmPerS,
|
|
|
|
|
|
Vector4 velocitySameAsStopMmPerS,
|
2018-02-02 09:47:06 -08:00
|
|
|
|
Vector4 speedMultiplier,
|
2018-01-17 14:03:56 -08:00
|
|
|
|
CancellationToken cancellationToken)
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2019-03-23 18:37:53 -07:00
|
|
|
|
if (FileTooBigToLoad(fileStream))
|
2017-06-21 23:26:20 -07:00
|
|
|
|
{
|
2019-03-23 18:37:53 -07:00
|
|
|
|
return new GCodeFileStreamed(fileStream);
|
2017-06-21 23:26:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2019-03-23 18:37:53 -07:00
|
|
|
|
return GCodeMemoryFile.Load(fileStream,
|
2018-01-17 14:03:56 -08:00
|
|
|
|
maxAccelerationMmPerS2,
|
|
|
|
|
|
maxVelocityMmPerS,
|
2018-02-02 09:47:06 -08:00
|
|
|
|
velocitySameAsStopMmPerS,
|
|
|
|
|
|
speedMultiplier,
|
2019-03-05 17:55:44 -08:00
|
|
|
|
cancellationToken,
|
|
|
|
|
|
null);
|
2017-06-21 23:26:20 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static string ReplaceNumberAfter(char charToReplaceAfter, string stringWithNumber, double numberToPutIn)
|
|
|
|
|
|
{
|
|
|
|
|
|
int charPos = stringWithNumber.IndexOf(charToReplaceAfter);
|
|
|
|
|
|
if (charPos != -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
int spacePos = stringWithNumber.IndexOf(" ", charPos);
|
|
|
|
|
|
if (spacePos == -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
string newString = string.Format("{0}{1:0.#####}", stringWithNumber.Substring(0, charPos + 1), numberToPutIn);
|
|
|
|
|
|
return newString;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
string newString = string.Format("{0}{1:0.#####}{2}", stringWithNumber.Substring(0, charPos + 1), numberToPutIn, stringWithNumber.Substring(spacePos));
|
|
|
|
|
|
return newString;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return stringWithNumber;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private static readonly bool Is32Bit = IntPtr.Size == 4;
|
2019-05-03 12:29:51 -07:00
|
|
|
|
}
|
2017-06-21 23:26:20 -07:00
|
|
|
|
}
|