using System; using System.Collections.Generic; using System.Globalization; using System.Diagnostics; using System.Text; using System.IO; using MatterHackers.Agg; using MatterHackers.Agg.UI; using MatterHackers.VectorMath; using MatterHackers.MatterControl.ContactForm; using MatterHackers.MatterControl.DataStorage; using MatterHackers.Localizations; namespace MatterHackers.MatterControl { public class SettingsLayer { //Container class representing a collection of setting along with the meta info for that collection public Dictionary settingsDictionary; public DataStorage.SliceSettingsCollection settingsCollectionData; public SettingsLayer(DataStorage.SliceSettingsCollection settingsCollection, Dictionary settingsDictionary) { this.settingsCollectionData = settingsCollection; this.settingsDictionary = settingsDictionary; } } public class ActiveSliceSettings { static ActiveSliceSettings globalInstance = null; static string configFileExtension = "ini"; private List activeSettingsLayers; public RootedObjectEventHandler CommitStatusChanged = new RootedObjectEventHandler(); public RootedObjectEventHandler SettingsChanged = new RootedObjectEventHandler(); private int settingsHashCode; bool hasUncommittedChanges = false; public bool HasUncommittedChanges { get { return hasUncommittedChanges; } set { if (this.hasUncommittedChanges != value) { this.hasUncommittedChanges = value; OnCommitStatusChanged(); } } } void OnCommitStatusChanged() { CommitStatusChanged.CallEvents(this, null); } void OnSettingsChanged() { //Set hash code back to 0 this.settingsHashCode = 0; SettingsChanged.CallEvents(this, null); } ActiveSliceSettings() { } public static ActiveSliceSettings Instance { get { if (globalInstance == null) { globalInstance = new ActiveSliceSettings(); globalInstance.LoadSettingsForPrinter(); } return globalInstance; } } public Vector2 GetPrintLevelSamplePosition(int index) { Vector2 bedSize = ActiveSliceSettings.Instance.BedSize; Vector2 printCenter = ActiveSliceSettings.Instance.PrintCenter; switch (BedShape) { case MeshVisualizer.MeshViewerWidget.BedShape.Circular: Vector2 firstPosition = new Vector2(printCenter.x, printCenter.y + (bedSize.y / 2) * .8); switch (index) { case 0: return firstPosition; case 1: return Vector2.Rotate(firstPosition, MathHelper.Tau / 3); case 2: return Vector2.Rotate(firstPosition, MathHelper.Tau * 2 / 3); default: throw new IndexOutOfRangeException(); } case MeshVisualizer.MeshViewerWidget.BedShape.Rectangular: default: switch (index) { case 0: return new Vector2(printCenter.x, printCenter.y + (bedSize.y / 2) * .8); case 1: return new Vector2(printCenter.x - (bedSize.x / 2) * .8, printCenter.y - (bedSize.y / 2) * .8); case 2: return new Vector2(printCenter.x + (bedSize.x / 2) * .8, printCenter.y - (bedSize.y / 2) * .8); default: throw new IndexOutOfRangeException(); } } } public void LoadSettingsForPrinter() { this.activeSettingsLayers = new List(); //Load default settings from the .ini file as first layer LoadDefaultConfigrationSettings(); //Load printer settings from database as second layer LoadPrinterConfigurationSettings(); #if false SetBedLevelEquation(0, 0, 0); #else if (PrinterCommunication.Instance.ActivePrinter != null) { PrintLeveling.Instance.SetPrintLevelingEquation( PrinterCommunication.Instance.GetPrintLevelingProbePosition(0), PrinterCommunication.Instance.GetPrintLevelingProbePosition(1), PrinterCommunication.Instance.GetPrintLevelingProbePosition(2), ActiveSliceSettings.Instance.PrintCenter); } #endif OnSettingsChanged(); this.HasUncommittedChanges = false; } public Dictionary DefaultSettings { get { return activeSettingsLayers[0].settingsDictionary; } } public bool Contains(string sliceSetting) { //Check whether the default settings (layer 0) contain a settings definition return DefaultSettings.ContainsKey(sliceSetting); } public double MaxFanSpeed { get { return ParseDouble(GetActiveValue("max_fan_speed")); } } public double MinFanSpeed { get { return ParseDouble(GetActiveValue("min_fan_speed")); } } public double FirstLayerHeight { get { string firstLayerValueString = GetActiveValue("first_layer_height"); if (firstLayerValueString.Contains("%")) { string onlyNumber = firstLayerValueString.Replace("%", ""); double ratio = ParseDouble(onlyNumber) / 100; return LayerHeight * ratio; } double firstLayerValue; firstLayerValue = ParseDouble(firstLayerValueString); return firstLayerValue; } } private static double ParseDouble(string firstLayerValueString) { double firstLayerValue; if (!double.TryParse(firstLayerValueString, out firstLayerValue)) { throw new Exception(string.Format("Format cannot be parsed. FirstLayerHeight '{0}'", firstLayerValueString)); } return firstLayerValue; } public double LayerHeight { get { return ParseDouble(GetActiveValue("layer_height")); } } public Vector2 GetBedSize() { return BedSize; } public Vector2 BedSize { get { return GetActiveVector2("bed_size"); } } public MeshVisualizer.MeshViewerWidget.BedShape BedShape { get { switch(GetActiveValue("bed_shape")) { case "rectangular": return MeshVisualizer.MeshViewerWidget.BedShape.Rectangular; case "circular": return MeshVisualizer.MeshViewerWidget.BedShape.Circular; default: throw new NotImplementedException(string.Format("'{0}' is not a known bed_shape.", GetActiveValue("bed_shape"))); } } } public Vector2 GetBedCenter() { return BedCenter; } public Vector2 BedCenter { get { return GetActiveVector2("print_center"); } } public double BuildHeight { get { return ParseDouble(GetActiveValue("build_height")); } } public Vector2 PrintCenter { get { return GetActiveVector2("print_center"); } } public double NozzleDiameter { get { return ParseDouble(GetActiveValue("nozzle_diameter")); } } public double FillamentDiameter { get { return ParseDouble(GetActiveValue("filament_diameter")); } } /// ///Returns the settings value at the 'top' of the stack /// public string GetActiveValue(string sliceSetting) { int numberOfActiveLayers = activeSettingsLayers.Count; //Go through settings layers one-by-one, in reverse order, until we find a layer that contains the value for (int i = numberOfActiveLayers - 1; i >= 0; i--) { if (activeSettingsLayers[i].settingsDictionary.ContainsKey(sliceSetting)) { return activeSettingsLayers[i].settingsDictionary[sliceSetting].Value; } } return "Unknown"; } public Vector2 GetActiveVector2(string sliceSetting) { string[] twoValues = GetActiveValue(sliceSetting).Split(','); if (twoValues.Length != 2) { throw new Exception(string.Format("Not parsing {0} as a Vector2", sliceSetting)); } Vector2 valueAsVector2 = new Vector2(); valueAsVector2.x = ParseDouble(twoValues[0]); valueAsVector2.y = ParseDouble(twoValues[1]); return valueAsVector2; } public void LoadPrinterConfigurationSettings() { if (PrinterCommunication.Instance.ActivePrinter != null) { DataStorage.SliceSettingsCollection collection; if (PrinterCommunication.Instance.ActivePrinter.DefaultSettingsCollectionId != 0) { int activePrinterSettingsID = PrinterCommunication.Instance.ActivePrinter.DefaultSettingsCollectionId; collection = DataStorage.Datastore.Instance.dbSQLite.Table().Where(v => v.Id == activePrinterSettingsID).Take(1).FirstOrDefault(); } else { collection = new DataStorage.SliceSettingsCollection(); collection.Name = PrinterCommunication.Instance.ActivePrinter.Name; collection.Commit(); PrinterCommunication.Instance.ActivePrinter.DefaultSettingsCollectionId = collection.Id; PrinterCommunication.Instance.ActivePrinter.Commit(); } SettingsLayer printerSettingsLayer = LoadConfigurationSettingsFromDatastore(collection); this.activeSettingsLayers.Add(printerSettingsLayer); } } private SettingsLayer LoadConfigurationSettingsFromDatastore(DataStorage.SliceSettingsCollection collection) { Dictionary settingsDictionary = new Dictionary(); IEnumerable settingsList = GetCollectionSettings(collection.Id); foreach (DataStorage.SliceSetting s in settingsList) { settingsDictionary[s.Name] = s; } SettingsLayer settingsLayer = new SettingsLayer(collection, settingsDictionary); return settingsLayer; } IEnumerable GetCollectionSettings(int collectionId) { //Retrieve a list of saved printers from the Datastore string query = string.Format("SELECT * FROM SliceSetting WHERE SettingsCollectionID = {0};", collectionId); IEnumerable result = (IEnumerable)DataStorage.Datastore.Instance.dbSQLite.Query(query); return result; } private void LoadDefaultConfigrationSettings() { string slic3rDefaultConfigurationPathAndFile = Path.Combine(ApplicationDataStorage.Instance.ApplicationStaticDataPath, "PrinterSettings", "config.ini"); DataStorage.SliceSettingsCollection defaultCollection = new DataStorage.SliceSettingsCollection(); defaultCollection.Name = "__default__"; SettingsLayer defaultSettingsLayer = LoadConfigurationSettingsFromFile(slic3rDefaultConfigurationPathAndFile, defaultCollection); this.activeSettingsLayers.Add(defaultSettingsLayer); } public bool LoadSettingsFromIni() { OpenFileDialogParams openParams = new OpenFileDialogParams("Load Slice Configuration|*." + configFileExtension); openParams.ActionButtonLabel = "Load Configuration"; openParams.Title = "MatterControl: Select A File"; FileDialog.OpenFileDialog(ref openParams); if (openParams.FileNames != null) { LoadConfigurationSettingsFromFileAsUnsaved(openParams.FileName); return true; } return false; } public SettingsLayer LoadConfigurationSettingsFromFile(string pathAndFileName, DataStorage.SliceSettingsCollection collection) { Dictionary settingsDictionary = new Dictionary(); SettingsLayer activeCollection; try { if (File.Exists(pathAndFileName)) { string[] lines = System.IO.File.ReadAllLines(pathAndFileName); foreach (string line in lines) { //Ignore commented lines if (!line.StartsWith("#")) { string[] settingLine = line.Split('='); string keyName = settingLine[0].Trim(); string settingDefaultValue = settingLine[1].Trim(); DataStorage.SliceSetting sliceSetting = new DataStorage.SliceSetting(); sliceSetting.Name = keyName; sliceSetting.Value = settingDefaultValue; settingsDictionary.Add(keyName, sliceSetting); } } activeCollection = new SettingsLayer(collection, settingsDictionary); return activeCollection; } return null; } catch (Exception e) { Debug.WriteLine(string.Format("Error loading configuration: {0}", e)); return null; } } public void LoadConfigurationSettingsFromFileAsUnsaved(string pathAndFileName) { try { if (File.Exists(pathAndFileName)) { string[] lines = System.IO.File.ReadAllLines(pathAndFileName); foreach (string line in lines) { //Ignore commented lines if (!line.StartsWith("#")) { string[] settingLine = line.Split('='); string keyName = settingLine[0].Trim(); string settingDefaultValue = settingLine[1].Trim(); //Add the setting to the active layer SaveValue(keyName, settingDefaultValue); } } } } catch (Exception e) { Debug.WriteLine(string.Format("Error loading configuration: {0}", e)); } } public void SaveValue(string keyName, string keyValue, int layerIndex=1) { SettingsLayer layer = this.activeSettingsLayers[layerIndex]; if (layer.settingsDictionary.ContainsKey(keyName) && layer.settingsDictionary[keyName].Value != keyValue) { layer.settingsDictionary[keyName].Value = keyValue; OnSettingsChanged(); HasUncommittedChanges = true; } else { DataStorage.SliceSetting sliceSetting = new DataStorage.SliceSetting(); sliceSetting.Name = keyName; sliceSetting.Value = keyValue; sliceSetting.SettingsCollectionId = layer.settingsCollectionData.Id; layer.settingsDictionary[keyName] = sliceSetting; OnSettingsChanged(); HasUncommittedChanges = true; } } public void CommitChanges() { for (int i = 1; i < this.activeSettingsLayers.Count; i++) { CommitLayerChanges(i); } HasUncommittedChanges = false; } public void CommitLayerChanges(int layerIndex) { SettingsLayer layer = this.activeSettingsLayers[layerIndex]; foreach (KeyValuePair item in layer.settingsDictionary) { item.Value.Commit(); } } public void SaveAs() { string documentsPath = System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal); SaveFileDialogParams saveParams = new SaveFileDialogParams("Save Slice Configuration|*." + configFileExtension,documentsPath); System.IO.Stream streamToSaveTo = FileDialog.SaveFileDialog (ref saveParams); if (streamToSaveTo != null) { streamToSaveTo.Close(); GenerateConfigFile(saveParams.FileName); } } public override int GetHashCode() { if (this.settingsHashCode == 0) { // make a new dictionary so we only hash on the current values and keys. StringBuilder bigStringForHashCode = new StringBuilder(); foreach (KeyValuePair setting in this.DefaultSettings) { string activeValue = GetActiveValue(setting.Key); bigStringForHashCode.Append(setting.Key); bigStringForHashCode.Append(activeValue); } this.settingsHashCode = bigStringForHashCode.ToString().GetHashCode(); } return this.settingsHashCode; } public void GenerateConfigFile(string fileName) { List configFileAsList = new List(); foreach (KeyValuePair setting in this.DefaultSettings) { string activeValue = GetActiveValue(setting.Key); string settingString = string.Format("{0} = {1}", setting.Key, activeValue); configFileAsList.Add(settingString); } string configFileAsString = string.Join("\n", configFileAsList.ToArray()); FileStream fs = new FileStream(fileName, FileMode.Create); StreamWriter sw = new System.IO.StreamWriter(fs); sw.Write(configFileAsString); sw.Close(); } public bool IsValid() { try { if (LayerHeight > NozzleDiameter) { string error = new LocalizedString("'Layer Height' must be less than or equal to the 'Nozzle Diameter'.").Translated; string details = string.Format("Layer Height = {0}\nNozzle Diameter = {1}", LayerHeight, NozzleDiameter); string location = new LocalizedString("Location: 'Advanced Controls' -> 'Slice Settings' -> 'Print' -> 'Layers/Perimeters'").Translated; StyledMessageBox.ShowMessageBox(string.Format("{0}\n\n{1}\n\n{2}", error, details, location), "Slice Error"); return false; } else if (FirstLayerHeight > NozzleDiameter) { string error = new LocalizedString("First Layer Height' must be less than or equal to the 'Nozzle Diameter'.").Translated; string details = string.Format("First Layer Height = {0}\nNozzle Diameter = {1}", FirstLayerHeight, NozzleDiameter); string location = "Location: 'Advanced Controls' -> 'Slice Settings' -> 'Print' -> 'Layers/Perimeters'"; StyledMessageBox.ShowMessageBox(string.Format("{0}\n\n{1}\n\n{2}", error, details, location), "Slice Error"); return false; } if (MinFanSpeed > 100) { string error = "The Min Fan Speed can only go as high as 100%."; string details = string.Format("It is currently set to {0}.", MinFanSpeed); string location = "Location: 'Advanced Controls' -> 'Slice Settings' -> 'Fillament' -> 'Cooling' (show all settings)"; StyledMessageBox.ShowMessageBox(string.Format("{0}\n\n{1}\n\n{2}", error, details, location), "Slice Error"); return false; } if (MaxFanSpeed > 100) { string error = "The Max Fan Speed can only go as high as 100%."; string details = string.Format("It is currently set to {0}.", MaxFanSpeed); string location = "Location: 'Advanced Controls' -> 'Slice Settings' -> 'Fillament' -> 'Cooling' (show all settings)"; StyledMessageBox.ShowMessageBox(string.Format("{0}\n\n{1}\n\n{2}", error, details, location), "Slice Error"); return false; } } catch(Exception e) { string stackTraceNoBackslashRs = e.StackTrace.Replace("\r", ""); ContactFormWindow.Open("Parse Error while slicing", e.Message + stackTraceNoBackslashRs); return false; } return true; } } }