using Aitex.Core.RT.Log; using Caliburn.Micro.Core; using MECF.Framework.Common.RecipeCenter; using MECF.Framework.UI.Client.CenterViews.Editors.Recipe; using RecipeEditorLib.RecipeModel.Params; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Reflection; using System.Text; using System.Windows; using System.Xml; namespace FurnaceUI.Views.Recipes { public class RecipeDataLayout : PropertyChangedBase { public bool IsChanged { get { bool changed = !IsSavedDesc; if (!changed) { //changed = ChkChanged(Steps) || ChkChanged(PopSettingSteps) || ChkChanged(StepTolerances); } if (!changed) { foreach (Param config in this.ConfigItems) { if (!config.IsSaved) { changed = true; break; } } } return changed; } } private bool _isSavedDesc; public bool IsSavedDesc { get { return _isSavedDesc; } set { this._isSavedDesc = value; this.NotifyOfPropertyChange("IsSavedDesc"); } } private string name; public string Name { get { return this.name; } set { this.name = value; this.NotifyOfPropertyChange("Name"); } } private string _chamberType; public string RecipeChamberType { get { return this._chamberType; } set { this._chamberType = value; this.NotifyOfPropertyChange("RecipeChamberType"); } } private string _recipePermission; public string RecipePermission { get { return this._recipePermission; } set { this._recipePermission = value; this.NotifyOfPropertyChange("RecipePermisson"); } } private string _recipeLevel; public string RecipeLevel { get { return this._recipeLevel; } set { this._recipeLevel = value; this.NotifyOfPropertyChange("RecipeLevel"); } } private string _recipeVersion; public string RecipeVersion { get { return this._recipeVersion; } set { this._recipeVersion = value; this.NotifyOfPropertyChange("RecipeVersion"); } } private string _prefixPath; public string PrefixPath { get { return _prefixPath; } set { _prefixPath = value; this.NotifyOfPropertyChange("PrefixPath"); } } private string creator; public string Creator { get { return this.creator; } set { this.creator = value; this.NotifyOfPropertyChange("Creator"); } } private DateTime createTime; public DateTime CreateTime { get { return this.createTime; } set { this.createTime = value; this.NotifyOfPropertyChange("CreateTime"); } } private string description; public string Description { get { return this.description; } set { this.description = value; this.NotifyOfPropertyChange("Description"); } } private string devisor; public string Revisor { get { return this.devisor; } set { this.devisor = value; this.NotifyOfPropertyChange("Revisor"); } } private DateTime deviseTime; public DateTime ReviseTime { get { return this.deviseTime; } set { this.deviseTime = value; this.NotifyOfPropertyChange("ReviseTime"); } } public ObservableCollection NormalSteps { get; private set; } = new ObservableCollection(); public ObservableCollection ExpertSteps { get; private set; } = new ObservableCollection(); public ObservableCollection ConfigItems { get; private set; } public ObservableCollection Configs { get; set; } private XmlDocument _doc; private string _module; public bool ToleranceEnable { get; set; } public bool IsCompatibleWithCurrentFormat { get; set; } private RecipeProvider _recipeProvider = new RecipeProvider(); private Dictionary> RecipeTemplate; public bool ChkChanged(Dictionary>> PopSteps) { //foreach (ObservableCollection> parameters in PopSteps.Values) //{ // if (ChkChanged(parameters)) // { // return true; // } //} return false; } public RecipeDataLayout() { ConfigItems = new ObservableCollection(); IsSavedDesc = true; _doc = new XmlDocument(); XmlElement node = _doc.CreateElement("Aitex"); _doc.AppendChild(node); node.AppendChild(_doc.CreateElement("TableRecipeData")); string vars = _recipeProvider.GetRecipeFormatXml(MECF.Framework.Common.Equipment.ModuleName.PM1); RecipeTemplate = _recipeProvider.GetGroupRecipeTemplate(); } public void Clear() { NormalSteps.Clear(); ExpertSteps.Clear(); ConfigItems.Clear(); RecipeChamberType = ""; RecipeVersion = ""; IsSavedDesc = true; _module = ""; } private void LoadSteps(XmlNodeList steps) { NormalSteps.Clear(); ExpertSteps.Clear(); foreach (XmlNode stepNode in steps) { if (stepNode.Attributes["Name"].Value == "Normal") { RecipeLayoutEntityNormal step = new RecipeLayoutEntityNormal(); //遍历Step节点 foreach (XmlAttribute att in stepNode.Attributes) { PropertyInfo pi = step.GetType().GetProperty(att.Name); if (pi != null && pi.Name == att.Name) { pi.SetValue(step, att.Value); } } NormalSteps.Add(step); } else { RecipeLayoutEntityExpert step = new RecipeLayoutEntityExpert(); //遍历Step节点 for (int i = 0; i < stepNode.Attributes.Count; i++) { BoatWaferItem item = new BoatWaferItem(); if (stepNode.Attributes[i].Name.StartsWith("slot")) { item.Slot = i + 1; item.Description = stepNode.Attributes[i].Value; step.Items.Add(item); } else { PropertyInfo pi = step.GetType().GetProperty(stepNode.Attributes[i].Name); if (pi.Name == stepNode.Attributes[i].Name) { pi.SetValue(step, stepNode.Attributes[i].Value); } } } ExpertSteps.Add(step); } } } public void InitData(string prefixPath, string recipeName, string recipeContent, ObservableCollection configDefine, string module) { IsCompatibleWithCurrentFormat = false; _module = module; Name = recipeName; PrefixPath = prefixPath; try { _doc = new XmlDocument(); _doc.LoadXml(recipeContent); if (!LoadHeader(_doc.SelectSingleNode("Aitex/TableRecipeData"))) return; XmlNodeList nodeSteps = _doc.SelectNodes($"Aitex/TableRecipeData/Module[@Name='{module}']/Step"); if (nodeSteps == null) nodeSteps = _doc.SelectNodes($"Aitex/TableRecipeData/Step"); LoadSteps(nodeSteps); XmlNode nodeConfig = _doc.SelectSingleNode($"Aitex/TableRecipeData/Module[@Name='{module}']/Config"); if (nodeSteps == null) nodeConfig = _doc.SelectSingleNode($"Aitex/TableRecipeData/Config"); LoadConfigs(configDefine, nodeConfig); IsCompatibleWithCurrentFormat = true; } catch (Exception ex) { LOG.Write(ex); } } private void LoadConfigs(ObservableCollection configDefine, XmlNode configNode) { ConfigItems.Clear(); foreach (var param in configDefine) { if (param is DoubleParam param1) { var config = new DoubleParam() { Name = param.Name, Value = param1.Value, DisplayName = param.DisplayName, Minimun = param1.Minimun, Maximun = param1.Maximun, Resolution = param1.Resolution }; if (configNode != null && configNode.Attributes[param1.Name] != null) config.Value = configNode.Attributes[param1.Name].Value; ConfigItems.Add(config); } if (param is NumParam paramInt) { var config = new NumParam() { Name = paramInt.Name, Value = paramInt.Value, DisplayName = paramInt.DisplayName, }; if (configNode != null && configNode.Attributes[paramInt.Name] != null) { int.TryParse(configNode.Attributes[paramInt.Name].Value, out int intValue); config.Value = intValue; } ConfigItems.Add(config); } if (param is StringParam paramString) { var config = new StringParam() { Name = param.Name, Value = paramString.Value, DisplayName = param.DisplayName, }; if (configNode != null && configNode.Attributes[paramString.Name] != null) config.Value = configNode.Attributes[paramString.Name].Value; ConfigItems.Add(config); } } } public string GetXmlString() { try { XmlElement nodeData = _doc.SelectSingleNode($"Aitex/TableRecipeData") as XmlElement; nodeData.SetAttribute("CreatedBy", Creator); nodeData.SetAttribute("CreationTime", CreateTime.ToString("yyyy-MM-dd HH:mm:ss")); nodeData.SetAttribute("LastRevisedBy", Revisor); nodeData.SetAttribute("LastRevisionTime", ReviseTime.ToString("yyyy-MM-dd HH:mm:ss")); nodeData.SetAttribute("Description", Description); nodeData.SetAttribute("RecipeChamberType", RecipeChamberType); nodeData.SetAttribute("RecipeVersion", RecipeVersion); nodeData.SetAttribute("RecipePermission", RecipePermission); nodeData.SetAttribute("RecipeLevel", RecipeLevel); XmlNode nodeModule = _doc.SelectSingleNode($"Aitex/TableRecipeData/Module[@Name='{_module}']"); if (nodeModule == null) { nodeModule = _doc.CreateElement("Module"); nodeData.AppendChild(nodeModule); } nodeModule.RemoveAll(); (nodeModule as XmlElement).SetAttribute("Name", _module); foreach (var parameters in NormalSteps) { XmlElement nodeStep = _doc.CreateElement("Step"); nodeStep.SetAttribute("Name", parameters.Name); //遍历属性 foreach (var prop in parameters.GetType().GetProperties()) { if (prop.Name == "IsNotifying") continue; string strValue = prop.GetValue(parameters) == null ? "" : prop.GetValue(parameters).ToString(); nodeStep.SetAttribute(prop.Name, strValue); } nodeModule.AppendChild(nodeStep); } foreach (var parameters in ExpertSteps) { XmlElement nodeStep = _doc.CreateElement("Step"); nodeStep.SetAttribute("Name", parameters.Name); for (int i = 0; i < parameters.Items.Count; i++) { nodeStep.SetAttribute($"slot{i + 1}", parameters.Items[i].Description); } nodeModule.AppendChild(nodeStep); } XmlElement nodeConfig = _doc.CreateElement("Config"); foreach (Param parameter in ConfigItems) { if (parameter.Visible == System.Windows.Visibility.Visible) { if (parameter is IntParam) nodeConfig.SetAttribute(parameter.Name, ((IntParam)parameter).Value.ToString()); if (parameter is NumParam) nodeConfig.SetAttribute(parameter.Name, ((NumParam)parameter).Value.ToString()); else if (parameter is DoubleParam) { string strValue = ((DoubleParam)parameter).Value; bool succed = double.TryParse(strValue, out double dValue); if (!succed) { MessageBox.Show($"The set value of {parameter.DisplayName} is {strValue}, not a valid value"); return null; } var config = ConfigItems.Where(m => m.Name == parameter.Name).FirstOrDefault(); if (config is DoubleParam param1) { if (param1.Minimun == 0 && param1.Maximun == 0) { //没有设定范围 } else if (dValue > param1.Maximun || dValue < param1.Minimun) { MessageBox.Show($"The set value of {parameter.DisplayName} is {dValue}, out of the range {param1.Minimun}~{param1.Maximun}"); return null; } } nodeConfig.SetAttribute(parameter.Name, ((DoubleParam)parameter).Value.ToString()); } else if (parameter is StringParam) nodeConfig.SetAttribute(parameter.Name, ((StringParam)parameter).Value.ToString()); else if (parameter is ComboxParam) nodeConfig.SetAttribute(parameter.Name, ((ComboxParam)parameter).Value.ToString()); else if (parameter is LoopComboxParam) nodeConfig.SetAttribute(parameter.Name, ((LoopComboxParam)parameter).Value.ToString()); else if (parameter is PositionParam) nodeConfig.SetAttribute(parameter.Name, ((PositionParam)parameter).Value.ToString()); else if (parameter is BoolParam) nodeConfig.SetAttribute(parameter.Name, ((BoolParam)parameter).Value.ToString()); else if (parameter is StepParam) nodeConfig.SetAttribute(parameter.Name, ((StepParam)parameter).Value.ToString()); else if (parameter is MultipleSelectParam) { List selected = new List(); ((MultipleSelectParam)parameter).Options.Apply( opt => { if (opt.IsChecked) selected.Add(opt.ControlName); } ); nodeConfig.SetAttribute(parameter.Name, string.Join(",", selected)); } } } nodeModule.AppendChild(nodeConfig); } catch (Exception ex) { LOG.Write(ex); return _doc.OuterXml; } return _doc.OuterXml; } public void SaveTo(string[] moduleList) { //GetXmlString(); XmlNode nodeModule = _doc.SelectSingleNode($"Aitex/TableRecipeData/Module[@Name='{_module}']"); if (nodeModule == null) { LOG.Write("recipe not find modules," + Name); return; } XmlNode nodeData = nodeModule.ParentNode; foreach (var module in moduleList) { if (module == _module) { continue; } var child = _doc.SelectSingleNode($"Aitex/TableRecipeData/Module[@Name='{module}']"); if (child != null) nodeData.RemoveChild(child); XmlElement node = nodeModule.Clone() as XmlElement; node.SetAttribute("Name", module); nodeData.AppendChild(node); } } public string DictionaryStepTime(Dictionary steptimes) { if (steptimes == null && steptimes.Count == 0) return null; StringBuilder rtn = new StringBuilder(); foreach (var item in steptimes.Keys) { if (steptimes[item] == "00:00:00") continue; rtn.Append($"{item},{steptimes[item]};"); } if (rtn.Length > 0) rtn.Remove(rtn.Length - 1, 1); return rtn.ToString(); } public Dictionary StepTimeToDictionary(string stepTime) { Dictionary steptimes = new Dictionary(); string[] tempTimes = stepTime.Split(';'); foreach (var item in tempTimes) { string[] subTimes = item.Split(','); steptimes.Add(subTimes[0], subTimes[1]); } return steptimes; } private bool LoadHeader(XmlNode nodeHeader) { if (nodeHeader == null) return false; if (nodeHeader.Attributes["CreatedBy"] != null) this.Creator = nodeHeader.Attributes["CreatedBy"].Value; if (nodeHeader.Attributes["CreationTime"] != null) this.CreateTime = DateTime.Parse(nodeHeader.Attributes["CreationTime"].Value); if (nodeHeader.Attributes["LastRevisedBy"] != null) this.Revisor = nodeHeader.Attributes["LastRevisedBy"].Value; if (nodeHeader.Attributes["LastRevisionTime"] != null) this.ReviseTime = DateTime.Parse(nodeHeader.Attributes["LastRevisionTime"].Value); if (nodeHeader.Attributes["Description"] != null) this.Description = nodeHeader.Attributes["Description"].Value; string chamberType = string.Empty; if (nodeHeader.Attributes["RecipeChamberType"] != null) chamberType = nodeHeader.Attributes["RecipeChamberType"].Value; if (!string.IsNullOrEmpty(chamberType) && chamberType != RecipeChamberType) { LOG.Write($"{chamberType} is not accordance with {RecipeChamberType}"); return false; } string version = string.Empty; if (nodeHeader.Attributes["RecipeVersion"] != null) version = nodeHeader.Attributes["RecipeVersion"].Value; //if (!string.IsNullOrEmpty(version) && version != RecipeVersion) //{ // LOG.Write($"{version} is not accordance with {RecipeVersion}"); // return false; //} return true; } } public class RecipeLayoutEntityNormal : PropertyChangedBase { public RecipeLayoutEntityNormal() { Name = "Normal"; } private string name; public string Name { get { return name; } set { name = value; this.NotifyOfPropertyChange(nameof(Name)); } } private string dummyUpperSlot; public string DummyUpperSlot { get { return dummyUpperSlot; } set { dummyUpperSlot = value; this.NotifyOfPropertyChange(nameof(DummyUpperSlot)); } } private string dummyLowerSlot; public string DummyLowerSlot { get { return dummyLowerSlot; } set { dummyLowerSlot = value; this.NotifyOfPropertyChange(nameof(DummyLowerSlot)); } } private string centeringSlotPosition; public string CenteringSlotPosition { get { return centeringSlotPosition; } set { centeringSlotPosition = value; this.NotifyOfPropertyChange(nameof(CenteringSlotPosition)); } } private string productPosition; public string ProductPosition { get { return productPosition; } set { productPosition = value; this.NotifyOfPropertyChange(nameof(ProductPosition)); } } private string productSlotNo; public string ProductSlotNo { get { return productSlotNo; } set { productSlotNo = value; this.NotifyOfPropertyChange(nameof(ProductSlotNo)); } } private string monitorPosition; public string MonitorPosition { get { return monitorPosition; } set { monitorPosition = value; this.NotifyOfPropertyChange(nameof(MonitorPosition)); } } private string monitor1SlotNo; public string Monitor1SlotNo { get { return monitor1SlotNo; } set { monitor1SlotNo = value; this.NotifyOfPropertyChange(nameof(Monitor1SlotNo)); } } private string monitor2SlotNo; public string Monitor2SlotNo { get { return monitor2SlotNo; } set { monitor2SlotNo = value; this.NotifyOfPropertyChange(nameof(Monitor2SlotNo)); } } private string sdDummySlotNo; public string SDDummySlotNo { get { return sdDummySlotNo; } set { sdDummySlotNo = value; this.NotifyOfPropertyChange(nameof(SDDummySlotNo)); } } private string fdDummySlotNo; public string FDDummySlotNo { get { return fdDummySlotNo; } set { fdDummySlotNo = value; this.NotifyOfPropertyChange(nameof(FDDummySlotNo)); } } private string whenPWaferShort = "LeaveAs"; public string WhenPWaferShort { get { return whenPWaferShort; } set { whenPWaferShort = value; this.NotifyOfPropertyChange(nameof(WhenPWaferShort)); } } private string sdRule = "DonotMove"; public string SDRule { get { return sdRule; } set { sdRule = value; this.NotifyOfPropertyChange(nameof(SDRule)); } } private string whenCassetteInBatchAreShort = "LeaveAs"; public string WhenCassetteInBatchAreShort { get { return whenCassetteInBatchAreShort; } set { whenCassetteInBatchAreShort = value; this.NotifyOfPropertyChange(nameof(WhenCassetteInBatchAreShort)); } } private string whenWaferInCassetteAreShort = "LeaveAs"; public string WhenWaferInCassetteAreShort { get { return whenWaferInCassetteAreShort; } set { whenWaferInCassetteAreShort = value; this.NotifyOfPropertyChange(nameof(WhenWaferInCassetteAreShort)); } } private string whenEDAreShort = "LeaveAs"; public string WhenEDAreShort { get { return whenEDAreShort; } set { whenEDAreShort = value; this.NotifyOfPropertyChange(nameof(WhenEDAreShort)); } } private string ruleOfSpaceInBoat = "LeaveAs"; public string RuleOfSpaceInBoat { get { return ruleOfSpaceInBoat; } set { ruleOfSpaceInBoat = value; this.NotifyOfPropertyChange(nameof(RuleOfSpaceInBoat)); } } } public class RecipeLayoutEntityExpert { public RecipeLayoutEntityExpert() { Name = "Expert"; } public string Name { get; set; } public List Items { get; set; } = new List(); } }