using Aitex.Common.Util; using Aitex.Core.RT.Event; using Aitex.Core.RT.Log; using Aitex.Core.Util; using Aitex.Core.Utilities; using Aitex.Core.WCF; using MECF.Framework.Common.Properties; using MECF.Framework.Common.RecipeCenter; using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Xml; using System.Xml.Schema; 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(); IRecipeFileContext _rcpContext; private ISequenceFileContext _seqContext; public void Initialize(IRecipeFileContext context) { Initialize(context, null, true); } public void Initialize(IRecipeFileContext context, bool enableService) { Initialize(context, null, enableService); } public void Initialize(IRecipeFileContext rcpContext, ISequenceFileContext seqContext, bool enableService) { _rcpContext = rcpContext == null ? new DefaultRecipeFileContext() : rcpContext; _seqContext = seqContext == null ? new DefaultSequenceFileContext() : seqContext; 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(); } } 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; } /// /// Check recipe content /// /// /// /// /// public bool CheckRecipe(string chamberId, string recipeContent, out List reasons) { 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 Dictionary> 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.Add(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); } #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; } public bool CheckRecipeFileExist(string chamberId, string recipeName) { return File.Exists(GenerateRecipeFilePath(chamberId, recipeName)); } /// /// 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).LastIndexOf("\\"); 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 { var path = GenerateRecipeFilePath(chamberId, recipeName); if (!_rcpContext.EnableEdit(path)) return false; File.Delete(path); 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 { var path = GenerateRecipeFilePath(chamId, newName); if (!_rcpContext.EnableEdit(path)) return false; if (File.Exists(path)) { 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); if (!_rcpContext.EnableEdit(path)) return false; FileInfo fi = new FileInfo(path); if (!fi.Directory.Exists) fi.Directory.Create(); XmlDocument xml = new XmlDocument(); xml.LoadXml(recipeContent); XmlTextWriter writer = new XmlTextWriter(path, null); 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; } /// /// 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; } /// /// 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, "创建recipe folder 出错"); WarningDialog(string.Format(Resources.RecipeFileManager_CreateFolder_RecipeFolder0CreateFailed, folderName)); return false; } return true; } /// /// 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, "重命名recipe folder 出错"); 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 bool CheckSequenceFileExist(string sequenceName ) { return File.Exists(GenerateSequenceFilePath(SequenceFolder, sequenceName)); } public string GetSequence(string sequenceName, bool needValidation) { string seq = string.Empty; try { using (StreamReader fs = new StreamReader(GenerateSequenceFilePath(SequenceFolder, sequenceName))) { seq = fs.ReadToEnd(); fs.Close(); } 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 { var path = GenerateSequenceFilePath(SequenceFolder, sequenceName); if (!_seqContext.EnableEdit(path)) return false; File.Delete(path); 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); if (!_seqContext.EnableEdit(path)) return false; FileInfo fi = new FileInfo(path); if (!fi.Directory.Exists) { fi.Directory.Create(); } XmlDocument xml = new XmlDocument(); xml.LoadXml(sequenceContent); 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 { var path = GenerateSequenceFilePath(SequenceFolder, oldName); if (!_seqContext.EnableEdit(path)) return false; if (File.Exists(GenerateSequenceFilePath(SequenceFolder, newName))) { EV.PostWarningLog(SourceModule, $"{newName} already exist, rename failed"); return false; } else { File.Move(path, 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"); } 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 } }