Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'UVtools.Core/GCode/GCodeBuilder.cs')
-rw-r--r--UVtools.Core/GCode/GCodeBuilder.cs286
1 files changed, 268 insertions, 18 deletions
diff --git a/UVtools.Core/GCode/GCodeBuilder.cs b/UVtools.Core/GCode/GCodeBuilder.cs
index 5e3d8a2..8bdbfff 100644
--- a/UVtools.Core/GCode/GCodeBuilder.cs
+++ b/UVtools.Core/GCode/GCodeBuilder.cs
@@ -80,6 +80,12 @@ namespace UVtools.Core.GCode
CentimetersPerMinute,
}
+ public enum GCodeMoveCommands : byte
+ {
+ G0, // Fast
+ G1 // Interpolated
+ }
+
public enum GCodeShowImageTypes : byte
{
FilenameZeroPNG,
@@ -100,6 +106,8 @@ namespace UVtools.Core.GCode
private GCodeShowImageTypes _gCodeShowImageType = GCodeShowImageTypes.FilenameNonZeroPNG;
private ushort _maxLedPower = byte.MaxValue;
private uint _lineCount;
+ private GCodeMoveCommands _layerMoveCommand;
+ private GCodeMoveCommands _endGCodeMoveCommand;
#endregion
@@ -129,6 +137,18 @@ namespace UVtools.Core.GCode
set => RaiseAndSetIfChanged(ref _gCodeShowImageType, value);
}
+ public GCodeMoveCommands LayerMoveCommand
+ {
+ get => _layerMoveCommand;
+ set => RaiseAndSetIfChanged(ref _layerMoveCommand, value);
+ }
+
+ public GCodeMoveCommands EndGCodeMoveCommand
+ {
+ get => _endGCodeMoveCommand;
+ set => RaiseAndSetIfChanged(ref _endGCodeMoveCommand, value);
+ }
+
public bool UseTailComma
{
get => _useTailComma;
@@ -303,7 +323,10 @@ namespace UVtools.Core.GCode
AppendLightOffM106();
if (raiseZ > 0)
{
- AppendMoveG0(raiseZ, feedRate);
+ if (_endGCodeMoveCommand == GCodeMoveCommands.G0)
+ AppendMoveG0(raiseZ, feedRate);
+ else
+ AppendMoveG1(raiseZ, feedRate);
}
AppendMotorsOff();
@@ -353,6 +376,22 @@ namespace UVtools.Core.GCode
AppendLine(CommandHomeG28);
}
+ public void AppendMoveGx(float z, float feedRate)
+ {
+ if(_layerMoveCommand == GCodeMoveCommands.G0)
+ AppendMoveG0(z, feedRate);
+ else
+ AppendMoveG1(z, feedRate);
+ }
+
+ public void AppendLiftMoveGx(float upZ, float upFeedRate, float downZ, float downFeedRate, float waitAfterLift = 0, float waitAfterRetract = 0)
+ {
+ if (_layerMoveCommand == GCodeMoveCommands.G0)
+ AppendLiftMoveG0(upZ, upFeedRate, downZ, downFeedRate, waitAfterLift, waitAfterRetract);
+ else
+ AppendLiftMoveG1(upZ, upFeedRate, downZ, downFeedRate, waitAfterLift, waitAfterRetract);
+ }
+
public void AppendMoveG0(float z, float feedRate)
{
@@ -360,18 +399,24 @@ namespace UVtools.Core.GCode
AppendLine(CommandMoveG0, z, feedRate);
}
- public void AppendLiftMoveG0(float upZ, float upFeedRate, float downZ, float downFeedRate, float stabilizationDelay = 0)
+ public void AppendLiftMoveG0(float upZ, float upFeedRate, float downZ, float downFeedRate, float waitAfterLift = 0, float waitAfterRetract = 0)
{
if (upZ == 0 || upFeedRate <= 0) return;
AppendLineOverrideComment(CommandMoveG0, "Z Lift", upZ, upFeedRate); // Z Lift
+
+ if (waitAfterLift > 0)
+ {
+ AppendWaitG4(waitAfterLift, "Wait after lift");
+ }
+
if (downZ != 0 && downFeedRate > 0)
{
AppendLineOverrideComment(CommandMoveG0, "Retract to layer height", downZ, downFeedRate);
}
- if (stabilizationDelay > 0)
+ if (waitAfterRetract > 0)
{
- AppendWaitG4(stabilizationDelay);
+ AppendWaitG4(waitAfterRetract, "Wait after retract");
}
}
@@ -381,18 +426,24 @@ namespace UVtools.Core.GCode
AppendLine(CommandMoveG1, z, feedRate);
}
- public void AppendLiftMoveG1(float upZ, float upFeedRate, float downZ, float downFeedRate, float stabilizationDelay = 0)
+ public void AppendLiftMoveG1(float upZ, float upFeedRate, float downZ, float downFeedRate, float waitAfterLift = 0, float waitAfterRetract = 0)
{
if (upZ == 0 || upFeedRate <= 0) return;
AppendLineOverrideComment(CommandMoveG1, "Lift Z", upZ, upFeedRate); // Z Lift
+
+ if (waitAfterLift > 0)
+ {
+ AppendWaitG4(waitAfterLift, "Wait after lift");
+ }
+
if (downZ != 0 && downFeedRate > 0)
{
AppendLineOverrideComment(CommandMoveG1, "Retract to layer height", downZ, downFeedRate);
}
- if (stabilizationDelay > 0)
+ if (waitAfterRetract > 0)
{
- AppendWaitG4(stabilizationDelay);
+ AppendWaitG4(waitAfterRetract, "Wait after retract");
}
}
@@ -444,6 +495,15 @@ namespace UVtools.Core.GCode
_ => throw new InvalidExpressionException($"Unhandled image type for {_gCodeShowImageType}")
};
+ public string GetShowImageString(string value) => _gCodeShowImageType switch
+ {
+ GCodeShowImageTypes.FilenameZeroPNG => $"{value}.png",
+ GCodeShowImageTypes.FilenameNonZeroPNG => $"{value}.png",
+ GCodeShowImageTypes.LayerIndexZero => $"{value}",
+ GCodeShowImageTypes.LayerIndexNonZero => $"{value}",
+ _ => throw new InvalidExpressionException($"Unhandled image type for {_gCodeShowImageType}")
+ };
+
public void RebuildGCode(FileFormat slicerFile, StringBuilder header) => RebuildGCode(slicerFile, header?.ToString());
public void RebuildGCode(FileFormat slicerFile, string header = null)
{
@@ -466,14 +526,17 @@ namespace UVtools.Core.GCode
for (uint layerIndex = 0; layerIndex < slicerFile.LayerCount; layerIndex++)
{
var layer = slicerFile[layerIndex];
+
+ float waitBeforeCure = ConvertFromSeconds(layer.WaitTimeBeforeCure);
float exposureTime = ConvertFromSeconds(layer.ExposureTime);
+ float waitAfterCure = ConvertFromSeconds(layer.WaitTimeAfterCure);
float liftHeight = layer.LiftHeight;
float liftZPos = Layer.RoundHeight(liftHeight + layer.PositionZ);
float liftZPosAbs = liftZPos;
float liftSpeed = ConvertFromMillimetersPerMinute(layer.LiftSpeed);
+ float waitAfterLift = ConvertFromSeconds(layer.WaitTimeAfterLift);
float retractPos = layer.PositionZ;
float retractSpeed = ConvertFromMillimetersPerMinute(layer.RetractSpeed);
- float lightOffDelay = ConvertFromSeconds(layer.LightOffDelay);
ushort pwmValue = layer.LightPWM;
if (_maxLedPower != byte.MaxValue)
{
@@ -497,24 +560,25 @@ namespace UVtools.Core.GCode
if (liftHeight > 0 && liftZPosAbs > layer.PositionZ)
{
- AppendLiftMoveG0(liftZPos, liftSpeed, retractPos, retractSpeed);
+ AppendLiftMoveGx(liftZPos, liftSpeed, retractPos, retractSpeed, waitAfterLift);
}
else if (lastZPosition < layer.PositionZ) // Ensure Z is on correct position
{
switch (GCodePositioningType)
{
case GCodePositioningTypes.Absolute:
- AppendMoveG0(layer.PositionZ, liftSpeed);
+ AppendMoveGx(layer.PositionZ, liftSpeed);
break;
case GCodePositioningTypes.Partial:
- AppendMoveG0(Layer.RoundHeight(layer.PositionZ - lastZPosition), liftSpeed);
+ AppendMoveGx(Layer.RoundHeight(layer.PositionZ - lastZPosition), liftSpeed);
break;
}
}
- AppendWaitG4(lightOffDelay, "Stabilization delay");
+ AppendWaitG4(waitBeforeCure, "Wait before cure"); // Safer to parse if present
AppendExposure(exposureTime, pwmValue);
+ if(waitAfterCure > 0) AppendWaitG4(waitAfterCure, "Wait after cure");
AppendLineIfCanComment(EndLayerComments, layerIndex, layer.PositionZ);
AppendLine();
@@ -522,7 +586,7 @@ namespace UVtools.Core.GCode
lastZPosition = layer.PositionZ;
}
- float finalRaiseZPosition = slicerFile.MachineZ;
+ float finalRaiseZPosition = Math.Max(lastZPosition, slicerFile.MachineZ);
switch (GCodePositioningType)
{
@@ -590,11 +654,190 @@ namespace UVtools.Core.GCode
if (string.IsNullOrWhiteSpace(gcode)) return;
}
- var positionType = ParsePositioningTypeFromGCode(gcode);
+ var positionType = GCodePositioningTypes.Absolute;
+ // test
+ /*gcode =
+ "G90\r\n" +
+ "M6054 \"1.png\";\r\n" +
+ "G4 P0;\r\n" +
+ "M106 S300;\r\n" +
+ "G4 P36000;\r\n" +
+ "M106 S0;\r\n" +
+ "G4 P5000;\r\n" +
+ "\r\n"
+ ;*/
float positionZ = 0;
- for (uint layerIndex = 0; layerIndex < slicerFile.LayerCount; layerIndex++)
+ var layerBlock = new GCodeLayer(slicerFile);
+
+ using var reader = new StringReader(gcode);
+ string line;
+ while ((line = reader.ReadLine()) != null)
+ {
+ if(string.IsNullOrWhiteSpace(line)) continue;
+
+ // Search for and switch position type when needed
+ if (line.StartsWith(CommandPositioningAbsoluteG90.Command))
+ {
+ positionType = GCodePositioningTypes.Absolute;
+ continue;
+ }
+
+ if (line.StartsWith(CommandPositioningPartialG91.Command))
+ {
+ positionType = GCodePositioningTypes.Partial;
+ continue;
+ }
+
+ var match = Regex.Match(line, CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(@"(\d+)")), RegexOptions.IgnoreCase);
+ if (match.Success && match.Groups.Count >= 2) // Begin new layer
+ {
+ var layerIndex = uint.Parse(match.Groups[1].Value);
+ if (_gCodeShowImageType is GCodeShowImageTypes.FilenameNonZeroPNG or GCodeShowImageTypes.LayerIndexNonZero) layerIndex--;
+ if (layerIndex > slicerFile.LayerCount)
+ {
+ throw new FileLoadException($"GCode parser detected the layer {layerIndex}, but the file was sliced to {slicerFile.LayerCount} layers.", slicerFile.FileFullPath);
+ }
+
+ // Propagate values before switch to the new layer
+ layerBlock.PositionZ ??= positionZ;
+ layerBlock.SetLayer();
+
+ layerBlock.Init();
+ layerBlock.LayerIndex = layerIndex;
+
+ continue;
+ }
+
+ if (!layerBlock.IsValid) continue; // No layer yet found to work on, skip
+
+
+ // Check moves
+ var moveG0Regex = Regex.Match(line, CommandMoveG0.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)"), RegexOptions.IgnoreCase);
+ var moveG1Regex = Regex.Match(line, CommandMoveG1.ToStringWithoutComments(@"([+-]?([0-9]*[.])?[0-9]+)", @"(([0-9]*[.])?[0-9]+)"), RegexOptions.IgnoreCase);
+ match = moveG0Regex.Success && moveG0Regex.Groups.Count >= 2 ? moveG0Regex : moveG1Regex;
+
+ if (match.Success && match.Groups.Count >= 4 && !layerBlock.RetractSpeed.HasValue)
+ {
+ float pos = float.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
+ float speed = ConvertToMillimetersPerMinute(float.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture));
+
+ if (!layerBlock.PositionZ.HasValue) // Lift or pos, set here for now
+ {
+ switch (positionType)
+ {
+ case GCodePositioningTypes.Absolute:
+ layerBlock.PositionZ = pos;
+ break;
+ case GCodePositioningTypes.Partial:
+ layerBlock.PositionZ = Layer.RoundHeight(positionZ + pos);
+ break;
+ }
+
+ layerBlock.LiftSpeed = speed;
+ continue;
+ }
+
+
+ // ~90% sure its retract here, still is possible to bug this with 2 lifts
+ layerBlock.RetractSpeed = speed;
+
+ switch (positionType)
+ {
+ case GCodePositioningTypes.Absolute:
+ layerBlock.LiftHeight = Layer.RoundHeight(layerBlock.PositionZ.Value - pos);
+ layerBlock.PositionZ = positionZ = pos;
+ break;
+ case GCodePositioningTypes.Partial:
+ layerBlock.LiftHeight = layerBlock.PositionZ - positionZ;
+ layerBlock.PositionZ = positionZ = Layer.RoundHeight(layerBlock.PositionZ.Value + pos);
+ break;
+ }
+
+ continue;
+ }
+
+ // Check for waits
+ match = Regex.Match(line, CommandWaitG4.ToStringWithoutComments(@"(([0-9]*[.])?[0-9]+)"), RegexOptions.IgnoreCase);
+ if (match.Success && match.Groups.Count >= 2)
+ {
+ var waitTime = float.Parse(match.Groups[1].Value);
+
+ if (layerBlock.PositionZ.HasValue && !layerBlock.RetractSpeed.HasValue) // Must be wait time after lift, if not, don't blame me!
+ {
+ layerBlock.WaitTimeAfterLift ??= 0;
+ layerBlock.WaitTimeAfterLift += ConvertToSeconds(waitTime);
+ continue;
+ }
+
+ if (!layerBlock.LightPWM.HasValue) // Before cure
+ {
+ layerBlock.WaitTimeBeforeCure ??= 0;
+ layerBlock.WaitTimeBeforeCure += ConvertToSeconds(waitTime);
+ continue;
+ }
+
+ if (layerBlock.IsExposing) // Must be exposure time, if not, don't blame me!
+ {
+ layerBlock.ExposureTime ??= 0;
+ layerBlock.ExposureTime += ConvertToSeconds(waitTime);
+ continue;
+ }
+
+ if (layerBlock.IsAfterLightOff)
+ {
+ if (!layerBlock.WaitTimeBeforeCure.HasValue) // Novamaker fix, delay on last line, broke logic but safer
+ {
+ layerBlock.WaitTimeBeforeCure ??= 0;
+ layerBlock.WaitTimeBeforeCure += ConvertToSeconds(waitTime);
+ }
+ else
+ {
+ layerBlock.WaitTimeAfterCure ??= 0;
+ layerBlock.WaitTimeAfterCure += ConvertToSeconds(waitTime);
+ }
+
+ continue;
+ }
+
+ continue;
+ }
+
+ // Check LightPWM
+ match = Regex.Match(line, CommandTurnLEDM106.ToStringWithoutComments(@"(\d+)"), RegexOptions.IgnoreCase);
+ if (match.Success && match.Groups.Count >= 2)
+ {
+ byte pwm;
+ if (_maxLedPower == byte.MaxValue)
+ {
+ pwm = byte.Parse(match.Groups[1].Value);
+ }
+ else
+ {
+ ushort pwmValue = ushort.Parse(match.Groups[1].Value);
+ pwm = (byte)(pwmValue * byte.MaxValue / _maxLedPower);
+ }
+
+ if (pwm == 0 && layerBlock.LightPWM.HasValue)
+ {
+ layerBlock.IsAfterLightOff = true;
+ }
+ else if(!layerBlock.IsAfterLightOff)
+ {
+ layerBlock.LightPWM = pwm;
+ }
+
+ continue;
+ }
+ }
+
+ // Propagate values of left over layer
+ layerBlock.PositionZ ??= positionZ;
+ layerBlock.SetLayer();
+
+
+ /*for (uint layerIndex = 0; layerIndex < slicerFile.LayerCount; layerIndex++)
{
var layer = slicerFile[layerIndex];
if(layer is null) continue;
@@ -602,7 +845,7 @@ namespace UVtools.Core.GCode
var endStr = CommandShowImageM6054.ToStringWithoutComments(GetShowImageString(layerIndex+1));
gcode = gcode.Substring(gcode.IndexOf(startStr, StringComparison.InvariantCultureIgnoreCase) + startStr.Length + 1);
var endStrIndex = gcode.IndexOf(endStr, StringComparison.Ordinal);
- var stripGcode = endStrIndex > 0 ? gcode[..endStrIndex] : gcode;/*.Trim(' ', '\n', '\r', '\t');*/
+ var stripGcode = endStrIndex > 0 ? gcode[..endStrIndex] : gcode;//.Trim(' ', '\n', '\r', '\t');
float liftHeight = 0;// this allow read back no lifts slicerFile.GetInitialLayerValueOrNormal(layerIndex, slicerFile.BottomLiftHeight, slicerFile.LiftHeight);
float liftSpeed = slicerFile.GetInitialLayerValueOrNormal(layerIndex, slicerFile.BottomLiftSpeed, slicerFile.LiftSpeed);
@@ -692,6 +935,7 @@ namespace UVtools.Core.GCode
layer.LightOffDelay = lightOffDelay;
layer.LightPWM = pwm;
}
+ */
if (rebuildGlobalTable)
{
@@ -700,22 +944,28 @@ namespace UVtools.Core.GCode
var bottomLayer = slicerFile.FirstLayer;
if (bottomLayer is not null)
{
+ if (bottomLayer.LightOffDelay > 0) slicerFile.BottomLightOffDelay = bottomLayer.LightOffDelay;
+ if (bottomLayer.WaitTimeBeforeCure > 0) slicerFile.BottomWaitTimeBeforeCure = bottomLayer.WaitTimeBeforeCure;
if (bottomLayer.ExposureTime > 0) slicerFile.BottomExposureTime = bottomLayer.ExposureTime;
+ if (bottomLayer.WaitTimeAfterCure > 0) slicerFile.BottomWaitTimeAfterCure = bottomLayer.WaitTimeAfterCure;
if (bottomLayer.LiftHeight > 0) slicerFile.BottomLiftHeight = bottomLayer.LiftHeight;
if (bottomLayer.LiftSpeed > 0) slicerFile.BottomLiftSpeed = bottomLayer.LiftSpeed;
+ if (bottomLayer.WaitTimeAfterLift > 0) slicerFile.BottomWaitTimeAfterLift = bottomLayer.WaitTimeAfterLift;
if (bottomLayer.RetractSpeed > 0) slicerFile.RetractSpeed = bottomLayer.RetractSpeed;
- if (bottomLayer.LightOffDelay > 0) slicerFile.BottomLightOffDelay = bottomLayer.LightOffDelay;
if (bottomLayer.LightPWM > 0) slicerFile.BottomLightPWM = bottomLayer.LightPWM;
}
var normalLayer = slicerFile.LastLayer;
if (normalLayer is not null)
{
+ if (normalLayer.LightOffDelay > 0) slicerFile.LightOffDelay = normalLayer.LightOffDelay;
+ if (normalLayer.WaitTimeBeforeCure > 0) slicerFile.WaitTimeBeforeCure = normalLayer.WaitTimeBeforeCure;
if (normalLayer.ExposureTime > 0) slicerFile.ExposureTime = normalLayer.ExposureTime;
+ if (normalLayer.WaitTimeAfterCure > 0) slicerFile.WaitTimeAfterCure = normalLayer.WaitTimeAfterCure;
if (normalLayer.LiftHeight > 0) slicerFile.LiftHeight = normalLayer.LiftHeight;
if (normalLayer.LiftSpeed > 0) slicerFile.LiftSpeed = normalLayer.LiftSpeed;
+ if (normalLayer.WaitTimeAfterLift > 0) slicerFile.WaitTimeAfterLift = normalLayer.WaitTimeAfterLift;
if (normalLayer.RetractSpeed > 0) slicerFile.RetractSpeed = normalLayer.RetractSpeed;
- if (normalLayer.LightOffDelay > 0) slicerFile.LightOffDelay = normalLayer.LightOffDelay;
if (normalLayer.LightPWM > 0) slicerFile.LightPWM = normalLayer.LightPWM;
}
});