using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms.VisualStyles; using System.Xml.Schema; using System.Xml; using System.IO; using Aitex.Core.RT.Log; using System.Text.RegularExpressions; using Aitex.Common.Util; using Aitex.Core.RT.Event; using Aitex.Core.Util; using Aitex.Core.Utilities; using Aitex.Core.WCF; using MECF.Framework.Common.OperationCenter; using MECF.Framework.Common.Properties; using MECF.Framework.Common.RecipeCenter; namespace Aitex.Core.RT.RecipeCenter { public class RecipeFileManager : Singleton { //sequence文件 统一放在 Recipes/Sequence 文件夹下面 public const string SequenceFolder = "Sequence"; public const string SourceModule = "Recipe"; private bool _recipeIsValid; private List _validationErrors = new List(); private List _validationWarnings = new List(); private Dictionary> _recipeItems; IRecipeFileContext _rcpContext; private ISequenceFileContext _seqContext; IEapMapFileContext _eapMapContext; public void Initialize(IRecipeFileContext context, IEapMapFileContext eapMapContext = null) { Initialize(context, null, true, eapMapContext); } public void Initialize(IRecipeFileContext context, bool enableService, IEapMapFileContext eapMapContext = null) { Initialize(context, null, enableService, eapMapContext); } public void Initialize(IRecipeFileContext rcpContext, ISequenceFileContext seqContext, bool enableService, IEapMapFileContext eapMapContext = null) { _rcpContext = rcpContext == null ? new DefaultRecipeFileContext() : rcpContext; _seqContext = seqContext == null ? new DefaultSequenceFileContext() : seqContext; _eapMapContext = eapMapContext == null ? new DefaultEapMapFileContext() : eapMapContext; CultureSupported.UpdateCoreCultureResource(CultureSupported.English); if (enableService) { Singleton.Instance.Initialize(new Type[] { typeof(RecipeService) }); } var dir = string.Format("{0}{1}\\", PathManager.GetRecipeDir(), SequenceFolder); DirectoryInfo di = new DirectoryInfo(dir); if (!di.Exists) { di.Create(); } InitMapConvert(); } private void ValidationEventHandler(object sender, ValidationEventArgs e) { switch (e.Severity) { case XmlSeverityType.Error: _validationErrors.Add(e.Message); _recipeIsValid = false; break; case XmlSeverityType.Warning: _validationWarnings.Add(e.Message); break; } } /// /// XML schema checking /// /// /// /// /// /// public bool ValidateRecipe(string chamberId, string recipeName, string recipeContent, out List reason) { try { XmlDocument document = new XmlDocument(); document.LoadXml(recipeContent); MemoryStream schemaStream = new MemoryStream(ASCIIEncoding.ASCII.GetBytes(GetRecipeSchema(chamberId))); XmlReader xmlSchemaReader = XmlReader.Create(schemaStream); XmlSchema schema1 = XmlSchema.Read(xmlSchemaReader, ValidationEventHandler); document.Schemas.Add(schema1); document.LoadXml(recipeContent); ValidationEventHandler eventHandler = new ValidationEventHandler(ValidationEventHandler); _recipeIsValid = true; _validationErrors = new List(); _validationWarnings = new List(); // Validates recipe. document.Validate(eventHandler); } catch (Exception ex) { LOG.Write(ex.Message); _recipeIsValid = false; } if (!_recipeIsValid && _validationErrors.Count == 0) { _validationErrors.Add(Resources.RecipeFileManager_ValidateRecipe_XMLSchemaValidateFailed); } reason = _validationErrors; return _recipeIsValid; } /// /// 检查变量ramp rate /// /// /// /// /// /// /// /// False:check ok, True: check failed public bool CheckRampRate(int stepNo, string rampEnable, string varName, string rampTime, double maxRampUpRate, double maxRampDownRate) { try { if (stepNo <= 0) return false; if (varName == "AZone.Setpoint" || varName == "BZone.Setpoint" || varName == "CZone.Setpoint" || varName == "DZone.Setpoint") { string curStepHeatCtrlMode = _recipeItems[stepNo]["Heater.Mode"]; string lastStepHeatCtrlMode = _recipeItems[stepNo - 1]["Heater.Mode"]; if (curStepHeatCtrlMode != lastStepHeatCtrlMode) return false; } bool isRampEnable = bool.Parse(rampEnable); string[] timeStr = rampTime.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries); double hh = 0; double mm = 0; double ss = 0; if (timeStr.Length == 3) { hh = double.Parse(timeStr[0]); mm = double.Parse(timeStr[1]); ss = double.Parse(timeStr[2]); } else if (timeStr.Length == 2) { mm = double.Parse(timeStr[0]); ss = double.Parse(timeStr[1]); } else if (timeStr.Length == 1) { ss = double.Parse(timeStr[0]); } double totalTimeSec = hh * 3600 + mm * 60 + ss; double diff = double.Parse(_recipeItems[stepNo][varName]) - double.Parse(_recipeItems[stepNo - 1][varName]); if (!isRampEnable || totalTimeSec <= 0) { //jump if (diff != 0) return true; return false; } else { double rampRate = diff / totalTimeSec; if ((rampRate > 0 && rampRate >= maxRampUpRate) || (rampRate < 0 && rampRate <= -maxRampDownRate)) return true; return false; } } catch (Exception ex) { LOG.Write(ex.Message); return true; } } private object _locker = new object(); /// /// Check recipe content /// /// /// /// /// public bool CheckRecipe(string chamberId, string recipeContent, out List reasons) { lock (_locker) { reasons = new List(); //add recipe content validation here try { var xmlFormat = new XmlDocument(); xmlFormat.LoadXml(GetRecipeFormatXml(chamberId)); var mfcDic = new Dictionary(); //name + scale var pcDic = new Dictionary(); //name + scale foreach (XmlElement mfc in xmlFormat.SelectNodes("/TableRecipeFormat/Catalog/Group/Step[@DeviceType='MFC']")) { mfcDic.Add(mfc.Attributes["ControlName"].Value, Convert.ToDouble(mfc.Attributes["Max"].Value)); } foreach (XmlElement pc in xmlFormat.SelectNodes("/TableRecipeFormat/Catalog/Group/Step[@DeviceType='PC']")) { pcDic.Add(pc.Attributes["ControlName"].Value, Convert.ToDouble(pc.Attributes["Max"].Value)); } //var spindleMaxSpeed = Convert.ToDouble(xmlFormat.SelectSingleNode("/TableRecipeFormat/Catalog/Group/Step[@ControlName='Spindle.Speed']").Attributes["Max"].Value); var xmlRecipe = new XmlDocument(); xmlRecipe.LoadXml(recipeContent); //read in all recipe items _recipeItems = new Dictionary>(); var stepElements = xmlRecipe.SelectNodes("/TableRecipeData/Step"); for (int stepNo = 0; stepNo < stepElements.Count; stepNo++) { var stepElement = stepElements[stepNo] as XmlElement; var step = new Dictionary(); _recipeItems[stepNo] = step; foreach (XmlAttribute att1 in stepElement.Attributes) { step.Add(att1.Name, att1.Value); } foreach (XmlElement subNd1 in stepElement.ChildNodes) { foreach (XmlAttribute att2 in subNd1.Attributes) { step.Add(att2.Name, att2.Value); } foreach (XmlElement subNd2 in subNd1.ChildNodes) { foreach (XmlAttribute att3 in subNd2.Attributes) { step.Add(att3.Name, att3.Value); } foreach (XmlElement subNd3 in subNd2.ChildNodes) { foreach (XmlAttribute att4 in subNd3.Attributes) { step.Add(att4.Name, att4.Value); } } } } } #region check loop control for (int j = 0; j < _recipeItems.Count; j++) { var loopStr = _recipeItems[j]["Loop"]; bool isLoopStart = Regex.IsMatch(loopStr, @"^Loop\x20\d+$"); bool isLoopEnd = Regex.IsMatch(loopStr, @"^Loop End$"); bool isNullOrEmpty = string.IsNullOrWhiteSpace(loopStr); if (!isLoopEnd && !isLoopStart && !isNullOrEmpty) { string reason = string.Format("Value '{0}' not valid", loopStr); reasons.Add(string.Format("第{0}步,{1}.", j + 1, reason)); } if (isLoopEnd) { string reason = "Loop Start 缺失"; reasons.Add(string.Format("第{0}步,{1}.", j + 1, reason)); } else if (isLoopStart) { for (int k = j + 1; k < _recipeItems.Count; k++) { var loopStr2 = _recipeItems[k]["Loop"]; bool isCurStepLoopStart = Regex.IsMatch(loopStr2, @"^Loop\x20\d+$"); bool isCurStepLoopEnd = Regex.IsMatch(loopStr2, @"^Loop End$"); isNullOrEmpty = string.IsNullOrWhiteSpace(loopStr2); if (!isCurStepLoopEnd && !isCurStepLoopStart && !isNullOrEmpty) { string reason = string.Format("Value '{0}' not valid", loopStr2); reasons.Add(string.Format("第{0}步,{1}.", k + 1, reason)); } else if (isCurStepLoopStart) { string reason = "前面循环没有结束,不能设置新的Loop Start标志"; reasons.Add(string.Format("第{0}步,{1}.", k + 1, reason)); } else if (isCurStepLoopEnd) { j = k; break; } if (k == _recipeItems.Count - 1) { j = k; string reason = "Loop End 缺失"; reasons.Add(string.Format("第{0}步,{1}.", k + 1, reason)); } } } } #endregion //check mfc range for (int stepNo = 0; stepNo < _recipeItems.Count; stepNo++) { foreach (var mfcName in mfcDic.Keys) { if (_recipeItems[stepNo].ContainsKey(mfcName)) { var mfcSetpoint = Convert.ToDouble(_recipeItems[stepNo][mfcName]); if (mfcSetpoint < 0 || mfcSetpoint > mfcDic[mfcName]) { reasons.Add(string.Format("第{0}步,{1}设定{2},超出 0~{3}sccm范围.", stepNo + 1, mfcName, mfcSetpoint, mfcDic[mfcName])); } } } //check pc range foreach (var pcName in pcDic.Keys) { if (_recipeItems[stepNo].ContainsKey(pcName)) { var pcSetpoint = Convert.ToDouble(_recipeItems[stepNo][pcName]); if (pcSetpoint < 0 || pcSetpoint > pcDic[pcName]) { reasons.Add(string.Format("第{0}步,{1}设定{2},超出 0~{3}mbar范围.", stepNo + 1, pcName, pcSetpoint, pcDic[pcName])); } } } } #region recipe parameter validation //reading predefined varaible var recipePredfine = new Dictionary(); foreach (XmlElement nd in xmlFormat.SelectNodes("/TableRecipeFormat/Validation/Predefine/Item")) { recipePredfine.Add(nd.Attributes["VarName"].Value, nd.Attributes["Value"].Value); } //lua validation //using (var lua = new LuaInterface.Lua()) //{ // //is special recipe? // bool IsSpecialRecipe = true; // { // for (int stepIndex = 0; stepIndex < _recipeItems.Count; stepIndex++) // { // if (_recipeItems[stepIndex]["Heater.Mode"] != "CurrentControl" || // _recipeItems[stepIndex]["AZone.Setpoint"] != "0" || // _recipeItems[stepIndex]["BZone.Setpoint"] != "0" || // _recipeItems[stepIndex]["CZone.Setpoint"] != "0" || // (_recipeItems[stepIndex].ContainsKey("DZone.Setpoint") && _recipeItems[stepIndex]["DZone.Setpoint"] != "0")) // { // IsSpecialRecipe = false; // break; // } // } // } // lua.DoString(string.Format("IsProductionRecipe={0};", (!IsSpecialRecipe).ToString().ToLower())); // //set get ramp rate function // lua.RegisterFunction("CheckRampRate", this, GetType().GetMethod("CheckRampRate")); // for (int stepIndex = 0; stepIndex < _recipeItems.Count; stepIndex++) // { // //reading var from current recipe step // foreach (var varName in _recipeItems[stepIndex].Keys) // { // string varValueString = _recipeItems[stepIndex][varName]; // double varValue; // if (double.TryParse(varValueString, out varValue)) // lua.DoString(string.Format("{0}={1};", varName.Replace(".", "_"), varValueString)); // else // lua.DoString(string.Format("{0}=\"{1}\";", varName.Replace(".", "_"), varValueString)); // } // //reading predefined variables in recipe // foreach (var key in recipePredfine.Keys) // { // string varValueString = recipePredfine[key]; // double varValue; // if (double.TryParse(varValueString, out varValue)) // lua.DoString(string.Format("{0}={1};", key, varValue)); // else // lua.DoString(string.Format("{0}=\"{1}\";", key, varValueString)); // } // //set stepNo // lua.DoString(string.Format("StepNo={0};", stepIndex)); // //reading validation rules // var validationRules = new List>(); // foreach (XmlElement nd in xmlFormat.SelectNodes("/TableRecipeFormat/Validation/Restriction/Rule")) // { // string varName = nd.Attributes["VarName"].Value; // string checkCondition = nd.Attributes["CheckCondition"].Value.Replace("<", "<").Replace(">", ">"); // string message = nd.Attributes["Message"].Value.Replace("'", "\""); // validationRules.Add(new Tuple(varName, checkCondition, message)); // } // //do valation // foreach (var rule in validationRules) // { // lua.DoString(string.Format("if {0} then hasErr=1 else hasErr=0 end", rule.Item2)); // bool hasError = ((int)lua.GetNumber("hasErr")) == 1; // if (hasError) // { // lua.DoString(string.Format("message=string.format({0});", rule.Item3)); // string reason = lua.GetString("message"); // reasons.Add(string.Format("第{0}步,{1}.", stepIndex + 1, reason)); // } // } // } //} #endregion } catch (Exception ex) { reasons.Add(Resources.RecipeFileManager_CheckRecipe_RecipeValidationFailed + ex.Message); LOG.Write(ex); return false; } return reasons.Count == 0; } } /// /// This method will be invoked by two places: /// (1) Load a recipe from server to GUI for editing (do not need validation when loading, do validation when saving); /// (2) Load a recipe from recipe engine to run process(always do a validation before run recipe); /// /// /// indicate whether a recipe format validation is needed or not /// public string LoadRecipe(string chamberId, string recipeName, bool needValidation) { string rcp = string.Empty; try { using (StreamReader fs = new StreamReader(GenerateRecipeFilePath(chamberId, recipeName))) { rcp = fs.ReadToEnd(); fs.Close(); } //if (needValidation) //{ // List reason; // if (!ValidateRecipe(chamberId, recipeName, rcp, out reason)) // { // rcp = string.Empty; // LOG.Write("校验recipe file 出错, " + string.Join(",", reason.ToArray())); // } //} } catch (Exception ex) { LOG.Write(ex, $"load recipe file failed, {recipeName}"); rcp = string.Empty; } return rcp; } /// /// Get recipe list /// /// /// /// public IEnumerable GetRecipes(string chamberId, bool includingUsedRecipe) { return _rcpContext.GetRecipes(chamberId, includingUsedRecipe); } /// /// Get recipe list in xml format /// /// /// /// public string GetXmlRecipeList(string chamberId, bool includingUsedRecipe) { XmlDocument doc = new XmlDocument(); var baseFolderPath = getRecipeDirPath(chamberId); DirectoryInfo curFolderInfo = new DirectoryInfo(baseFolderPath); doc.AppendChild(GenerateRecipeList(chamberId, curFolderInfo, doc, includingUsedRecipe)); return doc.OuterXml; } public void SaveRecipeHistory(string chamberId, string recipeName, string recipeContent, bool needSaveAs = true) { try { if (!string.IsNullOrEmpty(recipeName) && needSaveAs) { string newRecipeName = string.Format("HistoryRecipe\\{0}\\{1}", DateTime.Now.ToString("yyyyMM"), recipeName); SaveRecipe(chamberId, newRecipeName, recipeContent, true, false); LOG.Write(string.Format("{0}通知TM保存工艺程序{1}", chamberId, recipeName)); } } catch (Exception ex) { LOG.Write(ex, string.Format("保存{0}工艺程序{1}发生错误", chamberId, recipeName)); } } /// /// generate recipe list information in current directory /// /// /// /// /// XmlElement GenerateRecipeList(string chamberId, DirectoryInfo currentDir, XmlDocument doc, bool includingUsedRecipe) { int trimLength = getRecipeDirPath(chamberId).Length; XmlElement folderEle = doc.CreateElement("Folder"); folderEle.SetAttribute("Name", currentDir.FullName.Substring(trimLength)); DirectoryInfo[] dirInfos = currentDir.GetDirectories(); foreach (DirectoryInfo dirInfo in dirInfos) { if (!includingUsedRecipe && dirInfo.Name == "HistoryRecipe") continue; folderEle.AppendChild(GenerateRecipeList(chamberId, dirInfo, doc, includingUsedRecipe)); } FileInfo[] fileInfos = currentDir.GetFiles("*.rcp"); foreach (FileInfo fileInfo in fileInfos) { XmlElement fileNd = doc.CreateElement("File"); string fileStr = fileInfo.FullName.Substring(trimLength).TrimStart(new char[] { '\\' }); ; fileStr = fileStr.Substring(0, fileStr.LastIndexOf(".")); fileNd.SetAttribute("Name", fileStr); folderEle.AppendChild(fileNd); } return folderEle; } /// /// Delete a recipe by recipe name /// /// /// /// public bool DeleteRecipe(string chamberId, string recipeName) { try { File.Delete(GenerateRecipeFilePath(chamberId, recipeName)); InfoDialog(string.Format(Resources.RecipeFileManager_DeleteRecipe_RecipeFile0DeleteSucceeded, recipeName)); } catch (Exception ex) { LOG.Write(ex, "删除recipe file 出错"); WarningDialog(string.Format(Resources.RecipeFileManager_DeleteRecipe_RecipeFile0DeleteFailed, recipeName)); return false; } return true; } /// /// Rename recipe /// /// /// /// /// public bool RenameRecipe(string chamId, string oldName, string newName) { try { if (File.Exists(GenerateRecipeFilePath(chamId, newName))) { WarningDialog(string.Format(Resources.RecipeFileManager_RenameRecipe_RecipeFile0FileExisted, oldName)); return false; } else { File.Move(GenerateRecipeFilePath(chamId, oldName), GenerateRecipeFilePath(chamId, newName)); InfoDialog(string.Format(Resources.RecipeFileManager_RenameRecipe_RecipeFile0Renamed, oldName, newName)); } } catch (Exception ex) { LOG.Write(ex, "重命名recipe file 出错"); WarningDialog(string.Format(Resources.RecipeFileManager_RenameRecipe_RecipeFile0RenameFailed, oldName, newName)); return false; } return true; } //private void EventInfo(string message) //{ // _rcpContext.PostInfoEvent(message); //} //private void EventWarning(string message) //{ // _rcpContext.PostWarningEvent(message); //} //private void EventAlarm(string message) //{ // _rcpContext.PostAlarmEvent(message); //} private void InfoDialog(string message) { _rcpContext.PostInfoDialogMessage(message); } private void WarningDialog(string message) { _rcpContext.PostWarningDialogMessage(message); } //private void AlarmDialog(string message) //{ // _rcpContext.PostAlarmDialogMessage(message); //} private void EventDialog(string message, List reason) { string msg = message; foreach (var r in reason) { msg += "\r\n" + r; } _rcpContext.PostDialogEvent(msg); } /// /// get recipe's file path /// /// /// private string GenerateRecipeFilePath(string chamId, string recipeName) { return getRecipeDirPath(chamId) + recipeName + ".rcp"; } private string GenerateSequenceFilePath(string chamId, string recipeName) { return getRecipeDirPath(chamId) + recipeName + ".seq"; } /// /// get recipe's dir path /// /// /// private string getRecipeDirPath(string chamId) { var dir = string.Format("{0}{1}\\", PathManager.GetRecipeDir(), chamId); DirectoryInfo di = new DirectoryInfo(dir); if (!di.Exists) di.Create(); return dir; } /// /// delete a recipe folder /// /// /// /// public bool DeleteFolder(string chamId, string folderName) { try { Directory.Delete(getRecipeDirPath(chamId) + folderName, true); InfoDialog(string.Format(Resources.RecipeFileManager_DeleteFolder_RecipeFolder0DeleteSucceeded, folderName)); } catch (Exception ex) { LOG.Write(ex, "删除recipe folder 出错"); WarningDialog(string.Format("recipe folder {0} delete failed", folderName)); return false; } return true; } /// /// save as recipe content /// /// /// /// /// public bool SaveAsRecipe(string chamId, string recipeName, string recipeContent) { var path = GenerateRecipeFilePath(chamId, recipeName); if (File.Exists(path)) { WarningDialog(string.Format(Resources.RecipeFileManager_SaveAsRecipe_RecipeFile0savefailed, recipeName)); return false; } return SaveRecipe(chamId, recipeName, recipeContent, true, true); } /// /// save recipe content /// /// /// /// /// public bool SaveRecipe(string chamId, string recipeName, string recipeContent, bool clearBarcode, bool notifyUI) { //validate recipe format when saving a recipe file //var reasons1 = new List(); //var reasons2 = new List(); //ValidateRecipe(chamId, recipeName, recipeContent, out reasons1); //CheckRecipe(chamId, recipeContent, out reasons2); //reasons1.AddRange(reasons2); //if (reasons1.Count > 0) //{ // EventDialog(string.Format( Resources.RecipeFileManager_SaveRecipe_SaveRecipeContentError, recipeName), reasons1); //} bool ret = true; try { var path = GenerateRecipeFilePath(chamId, recipeName); FileInfo fi = new FileInfo(path); if (!fi.Directory.Exists) fi.Directory.Create(); XmlDocument xml = new XmlDocument(); xml.LoadXml(recipeContent); XmlTextWriter writer = new XmlTextWriter(path, Encoding.UTF8); writer.Formatting = Formatting.Indented; xml.Save(writer); writer.Close(); if (notifyUI) { InfoDialog(string.Format(Resources.RecipeFileManager_SaveRecipe_RecipeFile0SaveCompleted, recipeName)); } else { EV.PostMessage("System", EventEnum.GeneralInfo, string.Format(Resources.RecipeFileManager_SaveRecipe_RecipeFile0SaveCompleted, recipeName)); } } catch (Exception ex) { LOG.Write(ex, "保存recipe file 出错"); if (notifyUI) { WarningDialog(string.Format(Resources.RecipeFileManager_SaveRecipe_RecipeFile0SaveFailed, recipeName)); } ret = false; } return ret; } /// /// create a new recipe folder /// /// /// /// public bool CreateFolder(string chamId, string folderName) { try { Directory.CreateDirectory(getRecipeDirPath(chamId) + folderName); InfoDialog(string.Format(Resources.RecipeFileManager_CreateFolder_RecipeFolder0Created, folderName)); } catch (Exception ex) { LOG.Write(ex, "create recipe folder failed"); WarningDialog(string.Format(Resources.RecipeFileManager_CreateFolder_RecipeFolder0CreateFailed, folderName)); return false; } return true; } /// /// move recipe file /// /// /// /// public bool MoveRecipeFile(string chamId, string recipeName, string tragetFolderName, bool clearBarcode, bool notifyUI) { bool ret = true; try { var path = getRecipeDirPath(chamId); string fullFileName = path + recipeName + ".rcp"; string tragetFullFilePath = path + tragetFolderName; File.Move(fullFileName, tragetFullFilePath + "\\" + recipeName.Split('\\')[recipeName.Split('\\').Length - 1] + ".rcp"); if (notifyUI) { InfoDialog(string.Format(Resources.RecipeFileManager_MoveRecipe_RecipeFile0MoveCompleted, recipeName)); } else { EV.PostMessage("System", EventEnum.GeneralInfo, string.Format(Resources.RecipeFileManager_MoveRecipe_RecipeFile0MoveCompleted, recipeName)); } } catch (Exception ex) { LOG.Write(ex, "移动 recipe file 出错"); if (notifyUI) { WarningDialog(string.Format(Resources.RecipeFileManager_MoveRecipe_RecipeFile0MoveFailed, recipeName)); } ret = false; } return ret; } /// /// Rename recipe folder name /// /// /// /// /// public bool RenameFolder(string chamId, string oldName, string newName) { try { string oldPath = getRecipeDirPath(chamId) + oldName; string newPath = getRecipeDirPath(chamId) + newName; Directory.Move(oldPath, newPath); InfoDialog(string.Format(Resources.RecipeFileManager_RenameFolder_RecipeFolder0renamed, oldName, newName)); } catch (Exception ex) { LOG.Write(ex, "Rename recipe folder failed"); WarningDialog(string.Format(Resources.RecipeFileManager_RenameFolder_RecipeFolder0RenameFailed, oldName, newName)); return false; } return true; } private string GetRecipeBody(string chamberId, string nodePath) { if (_rcpContext == null) return string.Empty; string schema = _rcpContext.GetRecipeDefiniton(chamberId); XmlDocument dom = new XmlDocument(); dom.LoadXml(schema); XmlNode node = dom.SelectSingleNode(nodePath); return node.OuterXml; } /// /// get reactor's recipe format define file /// /// /// public string GetRecipeFormatXml(string chamberId) { return GetRecipeBody(chamberId, "/Aitex/TableRecipeFormat"); } /// /// get reactor's template recipe file /// /// /// public string GetRecipeTemplate(string chamberId) { if (_rcpContext != null) return _rcpContext.GetRecipeTemplate(chamberId); return GetRecipeBody(chamberId, "/Aitex/TableRecipeData"); } /// /// get reactor's template recipe file /// /// /// public string GetRecipeSchema(string chamberId) { if (_rcpContext == null) return string.Empty; string schema = _rcpContext.GetRecipeDefiniton(chamberId); XmlDocument dom = new XmlDocument(); dom.LoadXml(schema); XmlNode node = dom.SelectSingleNode("/Aitex/TableRecipeSchema"); return node.InnerXml; } public string GetRecipeByBarcode(string chamberId, string barcode) { try { string recipePath = PathManager.GetRecipeDir() + chamberId + "\\"; var di = new DirectoryInfo(recipePath); var fis = di.GetFiles("*.rcp", SearchOption.AllDirectories); XmlDocument xml = new XmlDocument(); foreach (var fi in fis) { string str = fi.FullName.Substring(recipePath.Length); if (!str.Contains("HistoryRecipe\\")) { xml.Load(fi.FullName); if (xml.SelectSingleNode(string.Format("/TableRecipeData[@Barcode='{0}']", barcode)) != null) { return str.Substring(0, str.LastIndexOf('.')); } } } return string.Empty; } catch (Exception ex) { LOG.Write(ex); return string.Empty; } } #region Sequence private string GetSequenceConfig(string nodePath) { if (_seqContext == null) return string.Empty; string schema = _seqContext.GetConfigXml(); XmlDocument dom = new XmlDocument(); dom.LoadXml(schema); XmlNode node = dom.SelectSingleNode(nodePath); return node.OuterXml; } public string GetSequence(string sequenceName, bool needValidation) { return GetSequence(sequenceName, needValidation, true); } public string GetSequence(string sequenceName, bool needValidation, bool isEap = false) { string seq = string.Empty; try { using (StreamReader fs = new StreamReader(GenerateSequenceFilePath(SequenceFolder, sequenceName))) { seq = fs.ReadToEnd(); fs.Close(); } if (isEap) { seq = PMToEapConvert(seq, isEap); } if (needValidation && !_seqContext.Validation(seq)) { EV.PostWarningLog(SourceModule, $"Read {sequenceName} failed, validation failed"); seq = string.Empty; } } catch (Exception ex) { LOG.Write(ex); EV.PostWarningLog(SourceModule, $"Read {sequenceName} failed, " + ex.Message); seq = string.Empty; } return seq; } public List GetSequenceNameList() { var result = new List(); try { string recipePath = PathManager.GetRecipeDir() + SequenceFolder + "\\"; var di = new DirectoryInfo(recipePath); var fis = di.GetFiles("*.seq", SearchOption.AllDirectories); foreach (var fi in fis) { string str = fi.FullName.Substring(recipePath.Length); str = str.Substring(0, str.LastIndexOf('.')); result.Add(str); } } catch (Exception ex) { LOG.Write(ex); EV.PostWarningLog(SourceModule, "Get sequence list failed, " + ex.Message); } return result; } public bool DeleteSequence(string sequenceName) { try { File.Delete(GenerateSequenceFilePath(SequenceFolder, sequenceName)); EV.PostInfoLog(SourceModule, $"sequence {sequenceName} deleted"); } catch (Exception ex) { LOG.Write(ex); EV.PostWarningLog(SourceModule, $"delete {sequenceName} failed, " + ex.Message); return false; } return true; } public bool SaveSequence(string sequenceName, string sequenceContent, bool notifyUI) { bool ret = true; try { var path = GenerateSequenceFilePath(SequenceFolder, sequenceName); FileInfo fi = new FileInfo(path); if (!fi.Directory.Exists) { fi.Directory.Create(); } XmlDocument xml = new XmlDocument(); xml.LoadXml(sequenceContent); EapToPMConvert(xml); XmlTextWriter writer = new XmlTextWriter(path, null); writer.Formatting = Formatting.Indented; xml.Save(writer); writer.Close(); if (notifyUI) { EV.PostPopDialogMessage(EventLevel.Information, "Save Complete", $"Sequence {sequenceName} saved "); } else { EV.PostInfoLog(SourceModule, $"Sequence {sequenceName} saved "); } } catch (Exception ex) { LOG.Write(ex); EV.PostWarningLog(SourceModule, $"save sequence {sequenceName} failed, " + ex.Message); if (notifyUI) { EV.PostPopDialogMessage(EventLevel.Alarm, "Save Error", $"save sequence {sequenceName} failed, " + ex.Message); } ret = false; } return ret; } public bool SaveAsSequence(string sequenceName, string sequenceContent) { var path = GenerateSequenceFilePath(SequenceFolder, sequenceName); if (File.Exists(path)) { EV.PostWarningLog(SourceModule, $"save sequence {sequenceName} failed, already exist"); return false; } return SaveSequence(sequenceName, sequenceContent, false); } public bool RenameSequence(string oldName, string newName) { try { if (File.Exists(GenerateSequenceFilePath(SequenceFolder, newName))) { EV.PostWarningLog(SourceModule, $"{newName} already exist, rename failed"); return false; } else { File.Move(GenerateSequenceFilePath(SequenceFolder, oldName), GenerateSequenceFilePath(SequenceFolder, newName)); EV.PostInfoLog(SourceModule, $"sequence {oldName} renamed to {newName}"); } } catch (Exception ex) { LOG.Write(ex); EV.PostWarningLog(SourceModule, $"rename {oldName} failed, " + ex.Message); return false; } return true; } public string GetSequenceFormatXml() { return GetSequenceConfig("/Aitex/TableSequenceFormat"); } private const string PMSelectionValue = "PMSelection"; private const string PositionValue = "Position"; private const string PMValue = "PM"; private const char commaChar = ','; public string GetEapMapXml() { return _eapMapContext.GetConfigXml(); } private string PMToEapConvert(string seq, bool isEap) { if (!string.IsNullOrWhiteSpace(seq) && isEap) { var pmToEapConvert = GetPMToEapConvert(); if(pmToEapConvert == null) { return seq; } XmlDocument doc = new XmlDocument(); doc.LoadXml(seq); XmlNodeList nodes = doc.SelectNodes("Aitex/TableSequenceData/Step"); foreach (XmlNode node in nodes) { switch (node.Attributes[PositionValue].Value) { case PMValue: { var pMSelectionValues = node.Attributes[PMSelectionValue].Value.Split(commaChar); for (int i = 0; i < pMSelectionValues.Length; i++) { if (pmToEapConvert.ContainsKey(pMSelectionValues[i])) { pMSelectionValues[i] = pmToEapConvert[pMSelectionValues[i]]; } } node.Attributes[PMSelectionValue].Value = string.Join(commaChar.ToString(), pMSelectionValues); break; } } } using (var ms = new MemoryStream()) using (var writer = new XmlTextWriter(ms, null)) { writer.Formatting = Formatting.Indented; doc.Save(writer); return Encoding.UTF8.GetString(ms.ToArray()); } } return seq; } private void EapToPMConvert(XmlDocument doc) { if(doc != null) { var eapToPMConvert = GetEapToPMConvert(); if(eapToPMConvert == null) { return; } XmlNodeList nodes = doc.SelectNodes("Aitex/TableSequenceData/Step"); foreach (XmlNode node in nodes) { switch (node.Attributes[PositionValue].Value) { case PMValue: { var pMSelectionValues = node.Attributes[PMSelectionValue].Value.Split(commaChar); for(int i=0;i< pMSelectionValues.Length; i++) { if (eapToPMConvert.ContainsKey(pMSelectionValues[i])) { pMSelectionValues[i] = eapToPMConvert[pMSelectionValues[i]]; } } node.Attributes[PMSelectionValue].Value = string.Join(commaChar.ToString(), pMSelectionValues); break; } } } } } private Dictionary _mapPMToEapConvert; private Dictionary _mapEapToPMConvert; public Dictionary GetEapToPMConvert() { if (_mapEapToPMConvert == null || _mapEapToPMConvert.Count == 0) { if (InitMapConvert()) { return _mapEapToPMConvert; } } return _mapEapToPMConvert; } public Dictionary GetPMToEapConvert() { if(_mapPMToEapConvert == null || _mapPMToEapConvert.Count == 0) { if (InitMapConvert()) { return _mapPMToEapConvert; } } return _mapPMToEapConvert; } private bool InitMapConvert() { try { if (_mapPMToEapConvert == null) { _mapPMToEapConvert = new Dictionary(); } if (_mapEapToPMConvert == null) { _mapEapToPMConvert = new Dictionary(); } var returnDic = new Dictionary(); var formatContent = GetEapMapXml(); XmlDocument doc = new XmlDocument(); doc.LoadXml(formatContent); XmlNodeList nodes = doc.SelectNodes("Aitex/EAPMap/Item"); const string nameValue = "Name"; const string eAPNameValue = "EAPName"; foreach (XmlNode node in nodes) { switch (node.Attributes[nameValue].Value) { case "PMMap": XmlNodeList multiitems = node.SelectNodes("Selection"); foreach (XmlNode item in multiitems) { _mapPMToEapConvert[item.Attributes[nameValue].Value] = item.Attributes[eAPNameValue].Value; _mapEapToPMConvert[item.Attributes[eAPNameValue].Value] = _mapPMToEapConvert[item.Attributes[nameValue].Value]; } break; } } } catch(Exception ex) { LOG.Write(ex, "Sequence Map exception"); return false; } return true; } internal bool DeleteSequenceFolder(string folderName) { try { Directory.Delete(PathManager.GetRecipeDir() + SequenceFolder + "\\" + folderName, true); EV.PostInfoLog(SourceModule, "Folder " + folderName + "deleted"); } catch (Exception ex) { LOG.Write(ex, "delete sequence folder exception"); EV.PostWarningLog(SourceModule, $"can not delete folder {folderName}, {ex.Message}"); return false; } return true; } internal bool CreateSequenceFolder(string folderName) { try { Directory.CreateDirectory(PathManager.GetRecipeDir() + SequenceFolder + "\\" + folderName); EV.PostInfoLog(SourceModule, "Folder " + folderName + "created"); } catch (Exception ex) { LOG.Write(ex, "sequence folder create exception"); EV.PostWarningLog(SourceModule, $"can not create folder {folderName}, {ex.Message}"); return false; } return true; } internal bool RenameSequenceFolder(string oldName, string newName) { try { string oldPath = PathManager.GetRecipeDir() + SequenceFolder + "\\" + oldName; string newPath = PathManager.GetRecipeDir() + SequenceFolder + "\\" + newName; Directory.Move(oldPath, newPath); EV.PostInfoLog(SourceModule, $"rename folder from {oldName} to {newName}"); } catch (Exception ex) { LOG.Write(ex, "rename sequence folder failed"); EV.PostWarningLog(SourceModule, $"can not rename folder {oldName}, {ex.Message}"); return false; } return true; } public string GetXmlSequenceList(string chamberId) { XmlDocument doc = new XmlDocument(); DirectoryInfo curFolderInfo = new DirectoryInfo(PathManager.GetRecipeDir() + SequenceFolder + "\\"); doc.AppendChild(GenerateSequenceList(chamberId, curFolderInfo, doc)); return doc.OuterXml; } XmlElement GenerateSequenceList(string chamberId, DirectoryInfo currentDir, XmlDocument doc) { int trimLength = (PathManager.GetRecipeDir() + SequenceFolder + "\\").Length; XmlElement folderEle = doc.CreateElement("Folder"); folderEle.SetAttribute("Name", currentDir.FullName.Substring(trimLength)); DirectoryInfo[] dirInfos = currentDir.GetDirectories(); foreach (DirectoryInfo dirInfo in dirInfos) { folderEle.AppendChild(GenerateSequenceList(chamberId, dirInfo, doc)); } FileInfo[] fileInfos = currentDir.GetFiles("*.seq"); foreach (FileInfo fileInfo in fileInfos) { XmlElement fileNd = doc.CreateElement("File"); string fileStr = fileInfo.FullName.Substring(trimLength).TrimStart(new char[] { '\\' }); ; fileStr = fileStr.Substring(0, fileStr.LastIndexOf(".")); fileNd.SetAttribute("Name", fileStr); folderEle.AppendChild(fileNd); } return folderEle; } #endregion } }