Exemplo n.º 1
0
        /// <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);
        }
Exemplo n.º 2
0
        /// <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);
        }