/// <summary> /// Validates the printer settings satisfy all requirements. /// </summary> /// <param name="printer">The printer to validate.</param> /// <returns>A list of all warnings and errors.</returns> public static List <ValidationError> ValidateSettings(this PrinterConfig printer, SettingsContext settings = null, bool validatePrintBed = true) { var fffPrinter = printer.Settings.Slicer.PrinterType == PrinterType.FFF; if (settings == null) { settings = new SettingsContext(printer, null, NamedSettingsLayers.All); } var errors = new List <ValidationError>(); var extruderCount = settings.GetValue <int>(SettingsKey.extruder_count); // Check to see if supports are required if (!settings.GetValue <bool>(SettingsKey.create_per_layer_support)) { var supportGenerator = new SupportGenerator(printer.Bed.Scene, .05); if (supportGenerator.RequiresSupport()) { errors.Add(new ValidationError(ValidationErrors.UnsupportedParts) { Error = "Possible Unsupported Parts Detected".Localize(), Details = "Some parts may require support structures to print correctly".Localize(), ErrorLevel = ValidationErrorLevel.Warning, FixAction = new NamedAction() { Title = "Generate Supports".Localize(), Action = () => { // Find and InvokeClick on the Generate Supports toolbar button var sharedParent = ApplicationController.Instance.DragDropData.View3DWidget.Parents <GuiWidget>().FirstOrDefault(w => w.Name == "View3DContainerParent"); if (sharedParent != null) { var supportsPopup = sharedParent.FindDescendant("Support SplitButton"); supportsPopup.InvokeClick(); } } } }); } } if (!settings.GetValue <bool>(SettingsKey.extruder_offset)) { var t0Offset = printer.Settings.Helpers.ExtruderOffset(0); if (t0Offset != Vector3.Zero) { errors.Add( new SettingsValidationError(SettingsKey.extruder_offset) { Error = "Nozzle 1 should have offsets set to 0.".Localize(), ValueDetails = "{0} = {1}\n{2} = {3}".FormatWith( GetSettingsName(SettingsKey.extruder_offset), settings.GetValue <double>(SettingsKey.extruder_offset), GetSettingsName(SettingsKey.extruder_offset), settings.GetValue <double>(SettingsKey.extruder_offset)), ErrorLevel = ValidationErrorLevel.Warning, }); } } // Check to see if current OEM layer matches downloaded OEM layer { if (printer.Settings.GetValue(SettingsKey.make) != "Other" && ProfileManager.GetOemSettingsNeedingUpdate(printer).Any()) { errors.Add(new ValidationError(ValidationErrors.SettingsUpdateAvailable) { Error = "Settings Update Available".Localize(), Details = "The default settings for this printer have changed and can be updated".Localize(), ErrorLevel = ValidationErrorLevel.Warning, FixAction = new NamedAction() { Title = "Update Settings...".Localize(), Action = () => { DialogWindow.Show(new UpdateSettingsPage(printer)); } } }); } } if (printer.Connection.IsConnected && !PrinterSetupRequired(printer) && validatePrintBed && errors.Count(e => e.ErrorLevel == ValidationErrorLevel.Error) == 0 && !printer.PrintableItems(printer.Bed.Scene).Any()) { errors.Add(new ValidationError(ValidationErrors.NoPrintableParts) { Error = "Empty Bed".Localize(), Details = "No printable parts exists within the bounds of the printer bed. Add content to continue".Localize(), ErrorLevel = ValidationErrorLevel.Error, }); } try { if (settings.GetValue <bool>(SettingsKey.validate_layer_height)) { if (settings.GetValue <double>(SettingsKey.layer_height) > settings.GetValue <double>(SettingsKey.nozzle_diameter)) { errors.Add( new SettingsValidationError(SettingsKey.layer_height) { Error = "{0} must be less than or equal to the {1}.".Localize().FormatWith( GetSettingsName(SettingsKey.layer_height), GetSettingsName(SettingsKey.nozzle_diameter)), ValueDetails = "{0} = {1}\n{2} = {3}".FormatWith( GetSettingsName(SettingsKey.layer_height), settings.GetValue <double>(SettingsKey.layer_height), GetSettingsName(SettingsKey.nozzle_diameter), settings.GetValue <double>(SettingsKey.nozzle_diameter)), }); } else if (settings.GetValue <double>(SettingsKey.layer_height) <= 0) { errors.Add( new SettingsValidationError(SettingsKey.layer_height) { Error = "{0} must be greater than 0.".Localize().FormatWith(GetSettingsName(SettingsKey.layer_height)), }); } // make sure the first layer height is not too big if (settings.GetValue <double>(SettingsKey.first_layer_height) > settings.GetValue <double>(SettingsKey.nozzle_diameter)) { errors.Add( new SettingsValidationError(SettingsKey.first_layer_height) { Error = "{0} must be less than or equal to the {1}.".Localize().FormatWith( GetSettingsName(SettingsKey.first_layer_height), GetSettingsName(SettingsKey.nozzle_diameter)), ValueDetails = "{0} = {1}\n{2} = {3}".FormatWith( GetSettingsName(SettingsKey.first_layer_height), settings.GetValue <double>(SettingsKey.first_layer_height), GetSettingsName(SettingsKey.nozzle_diameter), settings.GetValue <double>(SettingsKey.nozzle_diameter)), }); } // make sure the first layer height is not too small else if (settings.GetValue <double>(SettingsKey.first_layer_height) < settings.GetValue <double>(SettingsKey.nozzle_diameter) / 2) { errors.Add( new SettingsValidationError(SettingsKey.first_layer_height) { Error = "{0} should be greater than or equal to 1/2 the {1}.".Localize().FormatWith( GetSettingsName(SettingsKey.first_layer_height), GetSettingsName(SettingsKey.nozzle_diameter)), ValueDetails = "{0} = {1}\n1/2 {2} = {3}".FormatWith( GetSettingsName(SettingsKey.first_layer_height), settings.GetValue <double>(SettingsKey.first_layer_height), GetSettingsName(SettingsKey.nozzle_diameter), settings.GetValue <double>(SettingsKey.nozzle_diameter) / 2), ErrorLevel = ValidationErrorLevel.Warning, }); } } string[] startGCode = settings.GetValue(SettingsKey.start_gcode).Replace("\\n", "\n").Split('\n'); // Print recovery is incompatible with firmware leveling - ensure not enabled in startGCode if (settings.GetValue <bool>(SettingsKey.recover_is_enabled) && !settings.GetValue <bool>(SettingsKey.has_hardware_leveling)) { // Ensure we don't have hardware leveling commands in the start gcode. foreach (string startGCodeLine in startGCode) { if (startGCodeLine.StartsWith("G29")) { errors.Add( new SettingsValidationError(SettingsKey.start_gcode) { Error = "Start G-Code cannot contain G29 if Print Recovery is enabled.".Localize(), Details = "Your Start G-Code should not contain a G29 if you are planning on using Print Recovery. Change your start G-Code or turn off Print Recovery.".Localize(), }); } if (startGCodeLine.StartsWith("G30")) { errors.Add( new SettingsValidationError(SettingsKey.start_gcode) { Error = "Start G-Code cannot contain G30 if Print Leveling is enabled.".Localize(), Details = "Your Start G-Code should not contain a G30 if you are planning on using Print Recovery. Change your start G-Code or turn off Print Recovery.".Localize(), }); } } } var levelingEnabled = printer.Settings.GetValue <bool>(SettingsKey.print_leveling_enabled) & !settings.GetValue <bool>(SettingsKey.has_hardware_leveling); var levelingRequired = printer.Settings.GetValue <bool>(SettingsKey.print_leveling_required_to_print); if (levelingEnabled || levelingRequired) { // Ensure we don't have hardware leveling commands in the start gcode. foreach (string startGCodeLine in startGCode) { if (startGCodeLine.StartsWith("G29")) { errors.Add( new SettingsValidationError(SettingsKey.start_gcode) { Error = "Start G-Code cannot contain G29 if Print Leveling is enabled.".Localize(), Details = "Your Start G-Code should not contain a G29 if you are planning on using print leveling. Change your start G-Code or turn off print leveling.".Localize(), }); } if (startGCodeLine.StartsWith("G30")) { errors.Add( new SettingsValidationError(SettingsKey.start_gcode) { Error = "Start G-Code cannot contain G30 if Print Leveling is enabled.".Localize(), Details = "Your Start G-Code should not contain a G30 if you are planning on using print leveling. Change your start G-Code or turn off print leveling.".Localize(), }); } } bool heatedBed = printer.Settings.GetValue <bool>(SettingsKey.has_heated_bed); double bedTemperature = printer.Settings.GetValue <double>(SettingsKey.bed_temperature); if (heatedBed && printer.Connection.IsConnected && !PrinterSetupRequired(printer) && printer.Settings.Helpers.PrintLevelingData is PrintLevelingData levelingData && !levelingData.IssuedLevelingTempWarning && Math.Abs(bedTemperature - levelingData.BedTemperature) > 10 && !printer.Settings.Helpers.ValidateLevelingWithProbe) { errors.Add( new ValidationError(ValidationErrors.BedLevelingTemperature) { Error = "Bed Leveling Temperature".Localize(), Details = string.Format( "Bed Leveling data created at {0}°C versus current {1}°C".Localize(), levelingData.BedTemperature, bedTemperature), ErrorLevel = ValidationErrorLevel.Warning, FixAction = new NamedAction() { Title = "Recalibrate", Action = () => { UiThread.RunOnIdle(() => { DialogWindow.Show(new PrintLevelingWizard(printer)); }); }, IsEnabled = () => printer.Connection.IsConnected } }); } if (levelingEnabled && !settings.GetValue <bool>(SettingsKey.has_hardware_leveling) && settings.GetValue <bool>(SettingsKey.has_z_probe) && settings.GetValue <bool>(SettingsKey.use_z_probe) && settings.GetValue <bool>(SettingsKey.validate_leveling) && (settings.GetValue <double>(SettingsKey.validation_threshold) < .001 || settings.GetValue <double>(SettingsKey.validation_threshold) > .5)) { var threshold = settings.GetValue <double>(SettingsKey.validation_threshold); errors.Add( new SettingsValidationError(SettingsKey.validation_threshold) { Error = "The Validation Threshold mush be greater than 0 and less than .5mm.".Localize().FormatWith(threshold), ValueDetails = "{0} = {1}".FormatWith(GetSettingsName(SettingsKey.validation_threshold), threshold), }); } // check if the leveling data has too large a range if (printer.Settings.Helpers.PrintLevelingData.SampledPositions.Count > 3) { var minLevelZ = double.MaxValue; var maxLevelZ = double.MinValue; foreach (var levelPosition in printer.Settings.Helpers.PrintLevelingData.SampledPositions) { minLevelZ = Math.Min(minLevelZ, levelPosition.Z); maxLevelZ = Math.Max(maxLevelZ, levelPosition.Z); } var delta = maxLevelZ - minLevelZ; var maxDelta = printer.Settings.GetValue <double>(SettingsKey.nozzle_diameter) * 10; if (delta > maxDelta) { errors.Add( new ValidationError(ValidationErrors.BedLevelingMesh) { Error = "Leveling Data Warning".Localize(), Details = "The leveling data might be invalid. It changes by as much as {0:0.##}mm. Leveling calibration should be re-run".Localize().FormatWith(delta), ErrorLevel = ValidationErrorLevel.Warning, FixAction = new NamedAction() { Title = "Recalibrate", Action = () => { UiThread.RunOnIdle(() => { DialogWindow.Show(new PrintLevelingWizard(printer)); }); }, IsEnabled = () => printer.Connection.IsConnected } }); } } } printer.Settings.ForTools <double>(SettingsKey.baby_step_z_offset, (key, value, i) => { // Make sure the z offsets are not too big if (Math.Abs(value) > 2) { // Static path generation for non-SliceSettings value var location = "Location".Localize() + ":" + "\n" + "Controls".Localize() + "\n • " + "Movement".Localize() + "\n • " + "Z Offset".Localize(); errors.Add( new ValidationError(ValidationErrors.ZOffset) { Error = "Z Offset is too large.".Localize(), Details = string.Format( "{0}\n\n{1}", "The Z Offset for your printer, sometimes called Baby Stepping, is greater than 2mm and invalid. Clear the value and re-level the bed.".Localize(), location) }); } }); if (settings.GetValue <double>(SettingsKey.first_layer_extrusion_width) > settings.GetValue <double>(SettingsKey.nozzle_diameter) * 4) { errors.Add( new SettingsValidationError(SettingsKey.first_layer_extrusion_width) { Error = "{0} must be less than or equal to the {1} * 4.".Localize().FormatWith( GetSettingsName(SettingsKey.first_layer_extrusion_width), GetSettingsName(SettingsKey.nozzle_diameter)), ValueDetails = "{0} = {1}\n{2} * 4 = {3}".FormatWith( GetSettingsName(SettingsKey.first_layer_extrusion_width), settings.GetValue <double>(SettingsKey.first_layer_extrusion_width), GetSettingsName(SettingsKey.nozzle_diameter), settings.GetValue <double>(SettingsKey.nozzle_diameter) * 4) }); } if (settings.GetValue <double>(SettingsKey.first_layer_extrusion_width) <= 0) { errors.Add( new SettingsValidationError(SettingsKey.first_layer_extrusion_width) { Error = "{0} must be greater than 0.".Localize().FormatWith( GetSettingsName(SettingsKey.first_layer_extrusion_width)), ValueDetails = "{0} = {1}".FormatWith( GetSettingsName(SettingsKey.first_layer_extrusion_width), settings.GetValue <double>(SettingsKey.first_layer_extrusion_width)), }); } if (settings.GetValue <double>(SettingsKey.fill_density) <= 0) { errors.Add( new SettingsValidationError(SettingsKey.fill_density) { Error = "{0} should be greater than 0.".Localize().FormatWith( GetSettingsName(SettingsKey.fill_density)), ErrorLevel = ValidationErrorLevel.Warning, ValueDetails = "{0} = {1}".FormatWith( GetSettingsName(SettingsKey.fill_density), settings.GetValue <double>(SettingsKey.fill_density)), }); } if (settings.GetValue <double>(SettingsKey.perimeters) <= 0) { errors.Add( new SettingsValidationError(SettingsKey.perimeters) { Error = "{0} should be greater than 0.".Localize().FormatWith( GetSettingsName(SettingsKey.perimeters)), ErrorLevel = ValidationErrorLevel.Warning, ValueDetails = "{0} = {1}".FormatWith( GetSettingsName(SettingsKey.perimeters), settings.GetValue <double>(SettingsKey.perimeters)), }); } if (settings.GetValue <int>(SettingsKey.extruder_count) > 1 && settings.GetValue <double>(SettingsKey.wipe_tower_perimeters_per_extruder) < 3) { errors.Add( new SettingsValidationError(SettingsKey.wipe_tower_perimeters_per_extruder) { Error = "{0} should be greater than 2.".Localize().FormatWith( GetSettingsName(SettingsKey.wipe_tower_perimeters_per_extruder)), ErrorLevel = ValidationErrorLevel.Warning, ValueDetails = "{0} = {1}".FormatWith( GetSettingsName(SettingsKey.wipe_tower_perimeters_per_extruder), settings.GetValue <double>(SettingsKey.wipe_tower_perimeters_per_extruder)), }); } if (settings.GetValue <double>(SettingsKey.infill_overlap_perimeter) < -settings.GetValue <double>(SettingsKey.nozzle_diameter) || settings.GetValue <double>(SettingsKey.infill_overlap_perimeter) > settings.GetValue <double>(SettingsKey.nozzle_diameter)) { errors.Add( new SettingsValidationError(SettingsKey.infill_overlap_perimeter) { Error = "{0} must be greater than 0 and less than your nozzle diameter. You may be missing a '%'.".Localize().FormatWith( GetSettingsName(SettingsKey.infill_overlap_perimeter)), ValueDetails = "{0} = {1}, {2} = {3}".FormatWith( GetSettingsName(SettingsKey.infill_overlap_perimeter), settings.GetValue <double>(SettingsKey.infill_overlap_perimeter), GetSettingsName(SettingsKey.nozzle_diameter), settings.GetValue <double>(SettingsKey.nozzle_diameter)), }); } if (printer.Connection.IsConnected && printer.Settings?.Helpers.ComPort() == "Emulator" && fffPrinter) { errors.Add( new SettingsValidationError(SettingsKey.com_port, "Connected to Emulator".Localize()) { Error = "You are connected to the Emulator not an actual printer.".Localize(), ErrorLevel = ValidationErrorLevel.Warning, FixAction = new NamedAction() { Title = "Switch".Localize(), IsEnabled = () => !printer.Connection.Printing && !printer.Connection.Paused, Action = () => UiThread.RunOnIdle(() => { // make sure we are not connected or we can't change the port printer.Connection.Disable(); // User initiated connect attempt failed, show port selection dialog DialogWindow.Show(new SetupStepComPortOne(printer)); }) } }); } if (settings.GetValue <double>(SettingsKey.external_perimeter_extrusion_width) <= 0) { errors.Add( new SettingsValidationError(SettingsKey.external_perimeter_extrusion_width) { Error = "{0} must be greater than 0.".Localize().FormatWith( GetSettingsName(SettingsKey.external_perimeter_extrusion_width)), ValueDetails = "{0} = {1}".FormatWith( GetSettingsName(SettingsKey.external_perimeter_extrusion_width), settings.GetValue <double>(SettingsKey.external_perimeter_extrusion_width)), }); } if (settings.GetValue <double>(SettingsKey.min_fan_speed) > 100) { errors.Add( new SettingsValidationError(SettingsKey.min_fan_speed) { Error = "The {0} can only go as high as 100%.".Localize().FormatWith( GetSettingsName(SettingsKey.min_fan_speed)), ValueDetails = "It is currently set to {0}.".Localize().FormatWith( settings.GetValue <double>(SettingsKey.min_fan_speed)), }); } if (settings.GetValue <double>(SettingsKey.max_fan_speed) > 100) { errors.Add( new SettingsValidationError(SettingsKey.max_fan_speed) { Error = "The {0} can only go as high as 100%.".Localize().FormatWith( GetSettingsName(SettingsKey.max_fan_speed)), ValueDetails = "It is currently set to {0}.".Localize().FormatWith( settings.GetValue <double>(SettingsKey.max_fan_speed)), }); } if (extruderCount < 1) { errors.Add( new SettingsValidationError(SettingsKey.extruder_count) { Error = "The {0} must be at least 1.".Localize().FormatWith( GetSettingsName(SettingsKey.extruder_count)), ValueDetails = "It is currently set to {0}.".Localize().FormatWith(extruderCount), }); } if (settings.GetValue <double>(SettingsKey.fill_density) < 0 || settings.GetValue <double>(SettingsKey.fill_density) > 1) { errors.Add( new SettingsValidationError(SettingsKey.fill_density) { Error = "The {0} must be between 0 and 1.".Localize().FormatWith( GetSettingsName(SettingsKey.fill_density)), ValueDetails = "It is currently set to {0}.".Localize().FormatWith( settings.GetValue <double>(SettingsKey.fill_density)), }); } // marlin firmware can only take a max of 128 bytes in a single instruction, make sure no lines are longer than that ValidateGCodeLinesShortEnough(SettingsKey.cancel_gcode, printer, errors); ValidateGCodeLinesShortEnough(SettingsKey.connect_gcode, printer, errors); ValidateGCodeLinesShortEnough(SettingsKey.end_gcode, printer, errors); ValidateGCodeLinesShortEnough(SettingsKey.layer_gcode, printer, errors); ValidateGCodeLinesShortEnough(SettingsKey.pause_gcode, printer, errors); ValidateGCodeLinesShortEnough(SettingsKey.resume_gcode, printer, errors); ValidateGCodeLinesShortEnough(SettingsKey.start_gcode, printer, errors); // If the given speed is part of the current slice engine then check that it is greater than 0. ValidateGoodSpeedSettingGreaterThan0(SettingsKey.bridge_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.air_gap_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.external_perimeter_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.first_layer_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.infill_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.perimeter_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.small_perimeter_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.solid_infill_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.support_material_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.top_solid_infill_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.travel_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.retract_speed, printer, errors); } catch (Exception e) { errors.Add( new ValidationError(ValidationErrors.ExceptionDuringSliceSettingsValidation) { Error = "Unexpected error validating settings".Localize(), Details = e.Message }); } return(errors); }
/// <summary> /// Validates the printer settings satisfy all requirements. /// </summary> /// <param name="printer">The printer to validate.</param> /// <returns>A list of all warnings and errors.</returns> public static List <ValidationError> ValidateSettings(this PrinterConfig printer, SettingsContext settings = null, bool validatePrintBed = true) { if (settings == null) { settings = new SettingsContext(printer, null, NamedSettingsLayers.All); } var errors = new List <ValidationError>(); var extruderCount = settings.GetValue <int>(SettingsKey.extruder_count); // Check to see if supports are required var supportGenerator = new SupportGenerator(printer.Bed.Scene, .05); if (supportGenerator.RequiresSupport()) { errors.Add(new ValidationError("UnsupportedParts") { Error = "Possible Unsupported Parts Detected".Localize(), Details = "Some parts may require support structures to print correctly".Localize(), ErrorLevel = ValidationErrorLevel.Warning, FixAction = new NamedAction() { Title = "Generate Supports".Localize(), Action = () => { // Find and InvokeClick on the Generate Supports toolbar button var sharedParent = ApplicationController.Instance.DragDropData.View3DWidget.Parents <GuiWidget>().FirstOrDefault(w => w.Name == "View3DContainerParent"); if (sharedParent != null) { var supportsPopup = sharedParent.FindDescendant("Support SplitButton"); supportsPopup.InvokeClick(); } } } }); } if (printer.Connection.IsConnected && !PrinterSetupRequired(printer) && validatePrintBed && errors.Count(e => e.ErrorLevel == ValidationErrorLevel.Error) == 0 && !printer.PrintableItems(printer.Bed.Scene).Any()) { errors.Add(new ValidationError("NoPrintableParts") { Error = "Empty Bed".Localize(), Details = "No printable parts exists within the bounds of the printer bed. Add content to continue".Localize(), ErrorLevel = ValidationErrorLevel.Error, }); } try { if (settings.GetValue <bool>(SettingsKey.validate_layer_height)) { if (settings.GetValue <double>(SettingsKey.layer_height) > settings.GetValue <double>(SettingsKey.nozzle_diameter)) { errors.Add( new SettingsValidationError(SettingsKey.layer_height) { Error = "{0} must be less than or equal to the {1}.".Localize().FormatWith( GetSettingsName(SettingsKey.layer_height), GetSettingsName(SettingsKey.nozzle_diameter)), ValueDetails = "{0} = {1}\n{2} = {3}".FormatWith( GetSettingsName(SettingsKey.layer_height), settings.GetValue <double>(SettingsKey.layer_height), GetSettingsName(SettingsKey.nozzle_diameter), settings.GetValue <double>(SettingsKey.nozzle_diameter)), }); } else if (settings.GetValue <double>(SettingsKey.layer_height) <= 0) { errors.Add( new SettingsValidationError(SettingsKey.layer_height) { Error = "{0} must be greater than 0.".Localize().FormatWith(GetSettingsName(SettingsKey.layer_height)), }); } if (settings.GetValue <double>(SettingsKey.first_layer_height) > settings.GetValue <double>(SettingsKey.nozzle_diameter)) { errors.Add( new SettingsValidationError(SettingsKey.first_layer_height) { Error = "{0} must be less than or equal to the {1}.".Localize().FormatWith( GetSettingsName(SettingsKey.first_layer_height), GetSettingsName(SettingsKey.nozzle_diameter)), ValueDetails = "{0} = {1}\n{2} = {3}".FormatWith( GetSettingsName(SettingsKey.first_layer_height), settings.GetValue <double>(SettingsKey.first_layer_height), GetSettingsName(SettingsKey.nozzle_diameter), settings.GetValue <double>(SettingsKey.nozzle_diameter)), }); } } string[] startGCode = settings.GetValue(SettingsKey.start_gcode).Replace("\\n", "\n").Split('\n'); // Print recovery is incompatible with firmware leveling - ensure not enabled in startGCode if (settings.GetValue <bool>(SettingsKey.recover_is_enabled)) { // Ensure we don't have hardware leveling commands in the start gcode. foreach (string startGCodeLine in startGCode) { if (startGCodeLine.StartsWith("G29")) { errors.Add( new SettingsValidationError(SettingsKey.start_gcode) { Error = "Start G-Code cannot contain G29 if Print Recovery is enabled.".Localize(), Details = "Your Start G-Code should not contain a G29 if you are planning on using Print Recovery. Change your start G-Code or turn off Print Recovery.".Localize(), }); } if (startGCodeLine.StartsWith("G30")) { errors.Add( new SettingsValidationError(SettingsKey.start_gcode) { Error = "Start G-Code cannot contain G30 if Print Leveling is enabled.".Localize(), Details = "Your Start G-Code should not contain a G30 if you are planning on using Print Recovery. Change your start G-Code or turn off Print Recovery.".Localize(), }); } } } var levelingEnabled = printer.Settings.GetValue <bool>(SettingsKey.print_leveling_enabled); var levelingRequired = printer.Settings.GetValue <bool>(SettingsKey.print_leveling_required_to_print); if (levelingEnabled || levelingRequired) { // Ensure we don't have hardware leveling commands in the start gcode. foreach (string startGCodeLine in startGCode) { if (startGCodeLine.StartsWith("G29")) { errors.Add( new SettingsValidationError(SettingsKey.start_gcode) { Error = "Start G-Code cannot contain G29 if Print Leveling is enabled.".Localize(), Details = "Your Start G-Code should not contain a G29 if you are planning on using print leveling. Change your start G-Code or turn off print leveling.".Localize(), }); } if (startGCodeLine.StartsWith("G30")) { errors.Add( new SettingsValidationError(SettingsKey.start_gcode) { Error = "Start G-Code cannot contain G30 if Print Leveling is enabled.".Localize(), Details = "Your Start G-Code should not contain a G30 if you are planning on using print leveling. Change your start G-Code or turn off print leveling.".Localize(), }); } } bool heatedBed = printer.Settings.GetValue <bool>(SettingsKey.has_heated_bed); double bedTemperature = printer.Settings.GetValue <double>(SettingsKey.bed_temperature); if (heatedBed && printer.Connection.IsConnected && !PrinterSetupRequired(printer) && printer.Settings.Helpers.PrintLevelingData is PrintLevelingData levelingData && !levelingData.IssuedLevelingTempWarning && Math.Abs(bedTemperature - levelingData.BedTemperature) > 10) { errors.Add( new ValidationError("BedLevelingTemperature") { Error = "Bed Leveling Temperature".Localize(), Details = string.Format( "Bed Leveling data created at {0}°C versus current {1}°C".Localize(), levelingData.BedTemperature, bedTemperature), ErrorLevel = ValidationErrorLevel.Warning, FixAction = new NamedAction() { Title = "Recalibrate", Action = () => { UiThread.RunOnIdle(() => { DialogWindow.Show(new PrintLevelingWizard(printer)); }); }, IsEnabled = () => printer.Connection.IsConnected } }); } } // Make sure the z offsets are not too big if (Math.Abs(settings.GetValue <double>(SettingsKey.baby_step_z_offset)) > 2) { // Static path generation for non-SliceSettings value var location = "Location".Localize() + ":" + "\n" + "Controls".Localize() + "\n • " + "Movement".Localize() + "\n • " + "Z Offset".Localize(); errors.Add( new ValidationError("ZOffset0TooLarge") { Error = "Z Offset is too large.".Localize(), Details = string.Format( "{0}\n\n{1}", "The Z Offset for your printer, sometimes called Baby Stepping, is greater than 2mm and invalid. Clear the value and re-level the bed.".Localize(), location) }); } if (settings.GetValue <double>(SettingsKey.first_layer_extrusion_width) > settings.GetValue <double>(SettingsKey.nozzle_diameter) * 4) { errors.Add( new SettingsValidationError(SettingsKey.first_layer_extrusion_width) { Error = "{0} must be less than or equal to the {1} * 4.".Localize().FormatWith( GetSettingsName(SettingsKey.first_layer_extrusion_width), GetSettingsName(SettingsKey.nozzle_diameter)), ValueDetails = "{0} = {1}\n{2} = {3}".FormatWith( GetSettingsName(SettingsKey.first_layer_extrusion_width), settings.GetValue <double>(SettingsKey.first_layer_extrusion_width), GetSettingsName(SettingsKey.nozzle_diameter), settings.GetValue <double>(SettingsKey.nozzle_diameter)) }); } if (settings.GetValue <double>(SettingsKey.first_layer_extrusion_width) <= 0) { errors.Add( new SettingsValidationError(SettingsKey.first_layer_extrusion_width) { Error = "{0} must be greater than 0.".Localize().FormatWith( GetSettingsName(SettingsKey.first_layer_extrusion_width)), ValueDetails = "{0} = {1}".FormatWith( GetSettingsName(SettingsKey.first_layer_extrusion_width), settings.GetValue <double>(SettingsKey.first_layer_extrusion_width)), }); } if (settings.GetValue <double>(SettingsKey.external_perimeter_extrusion_width) > settings.GetValue <double>(SettingsKey.nozzle_diameter) * 4) { errors.Add( new SettingsValidationError(SettingsKey.external_perimeter_extrusion_width) { Error = "{0} must be less than or equal to the {1} * 4.".Localize().FormatWith( GetSettingsName(SettingsKey.external_perimeter_extrusion_width), GetSettingsName(SettingsKey.nozzle_diameter)), ValueDetails = "{0} = {1}\n{2} = {3}".FormatWith( GetSettingsName(SettingsKey.external_perimeter_extrusion_width), settings.GetValue <double>(SettingsKey.external_perimeter_extrusion_width), GetSettingsName(SettingsKey.nozzle_diameter), settings.GetValue <double>(SettingsKey.nozzle_diameter)), }); } if (settings.GetValue <double>(SettingsKey.external_perimeter_extrusion_width) <= 0) { errors.Add( new SettingsValidationError(SettingsKey.external_perimeter_extrusion_width) { Error = "{0} must be greater than 0.".Localize().FormatWith( GetSettingsName(SettingsKey.external_perimeter_extrusion_width)), ValueDetails = "{0} = {1}".FormatWith( GetSettingsName(SettingsKey.external_perimeter_extrusion_width), settings.GetValue <double>(SettingsKey.external_perimeter_extrusion_width)), }); } if (settings.GetValue <double>(SettingsKey.min_fan_speed) > 100) { errors.Add( new SettingsValidationError(SettingsKey.min_fan_speed) { Error = "The {0} can only go as high as 100%.".Localize().FormatWith( GetSettingsName(SettingsKey.min_fan_speed)), ValueDetails = "It is currently set to {0}.".Localize().FormatWith( settings.GetValue <double>(SettingsKey.min_fan_speed)), }); } if (settings.GetValue <double>(SettingsKey.max_fan_speed) > 100) { errors.Add( new SettingsValidationError(SettingsKey.max_fan_speed) { Error = "The {0} can only go as high as 100%.".Localize().FormatWith( GetSettingsName(SettingsKey.max_fan_speed)), ValueDetails = "It is currently set to {0}.".Localize().FormatWith( settings.GetValue <double>(SettingsKey.max_fan_speed)), }); } if (extruderCount < 1) { errors.Add( new SettingsValidationError(SettingsKey.extruder_count) { Error = "The {0} must be at least 1.".Localize().FormatWith( GetSettingsName(SettingsKey.extruder_count)), ValueDetails = "It is currently set to {0}.".Localize().FormatWith(extruderCount), }); } if (settings.GetValue <double>(SettingsKey.fill_density) < 0 || settings.GetValue <double>(SettingsKey.fill_density) > 1) { errors.Add( new SettingsValidationError(SettingsKey.fill_density) { Error = "The {0} must be between 0 and 1.".Localize().FormatWith( GetSettingsName(SettingsKey.fill_density)), ValueDetails = "It is currently set to {0}.".Localize().FormatWith( settings.GetValue <double>(SettingsKey.fill_density)), }); } // marlin firmware can only take a max of 128 bytes in a single instruction, make sure no lines are longer than that ValidateGCodeLinesShortEnough(SettingsKey.cancel_gcode, printer, errors); ValidateGCodeLinesShortEnough(SettingsKey.connect_gcode, printer, errors); ValidateGCodeLinesShortEnough(SettingsKey.end_gcode, printer, errors); ValidateGCodeLinesShortEnough(SettingsKey.layer_gcode, printer, errors); ValidateGCodeLinesShortEnough(SettingsKey.pause_gcode, printer, errors); ValidateGCodeLinesShortEnough(SettingsKey.resume_gcode, printer, errors); ValidateGCodeLinesShortEnough(SettingsKey.start_gcode, printer, errors); // If the given speed is part of the current slice engine then check that it is greater than 0. ValidateGoodSpeedSettingGreaterThan0(SettingsKey.bridge_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.air_gap_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.external_perimeter_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.first_layer_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.infill_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.perimeter_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.small_perimeter_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.solid_infill_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.support_material_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.top_solid_infill_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.travel_speed, printer, errors); ValidateGoodSpeedSettingGreaterThan0(SettingsKey.retract_speed, printer, errors); } catch (Exception e) { errors.Add( new ValidationError("ExceptionDuringSliceSettingsValidation") { Error = "Unexpected error validating settings".Localize(), Details = e.Message }); } return(errors); }