SicMultiplate/Modules/Mainframe/PMs/RecipeExecutions/RecipeParser.cs

594 lines
26 KiB
C#
Raw Normal View History

2023-03-28 13:24:11 +08:00
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml;
using Aitex.Common.Util;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.RecipeCenter;
using Aitex.Core.RT.SCCore;
using static Aitex.Core.RT.Device.PmDevices.DicMode;
2023-04-13 15:35:13 +08:00
namespace SicModules.PMs.RecipeExecutions
2023-03-28 13:24:11 +08:00
{
public class RecipeDataColumn
{
public string disPlay { get; set; }
public double maxNum { get; set; }
public double minMum { get; set; }
}
public class RecipeParser
{
public static bool Parse(string recipeFile, string module, out RecipeHead recipeHead, out List<RecipeStep> recipeData, out string reason)
{
reason = string.Empty;
recipeHead = new RecipeHead();
recipeData = new List<RecipeStep>();
string content = RecipeFileManager.Instance.LoadRecipe("", recipeFile, false);
if (string.IsNullOrEmpty(content))
{
reason = $"{recipeFile} is not a valid recipe file";
return false;
}
try
{
//获取工艺程序文件中允许的命令字符串列表
//目的:如果工艺程序文件中含有规定之外的命令,则被禁止执行
HashSet<string> recipeAllowedCommands = new HashSet<string>();
XmlDocument rcpFormatDoc = new XmlDocument();
string recipeSchema = PathManager.GetCfgDir() + @"\Recipe\Sic\Process\RecipeFormat.xml";
rcpFormatDoc.Load(recipeSchema);
XmlNodeList rcpItemNodeList = rcpFormatDoc.SelectNodes("/Aitex/TableRecipeFormat/Catalog/Group/Step");
foreach (XmlElement item in rcpItemNodeList)
recipeAllowedCommands.Add(item.Attributes["ControlName"].Value);
//获取工艺程序文件中所有步的内容
XmlDocument rcpDataDoc = new XmlDocument();
rcpDataDoc.LoadXml(content);
XmlNode nodeModule = rcpDataDoc.SelectSingleNode($"/Aitex/TableRecipeData/Module[@Name='{module}']");
if (nodeModule == null)
{
reason = $"Recipe file does not contains step content for {module}";
return false;
}
XmlElement nodeConfig = nodeModule.SelectSingleNode($"Config") as XmlElement;
recipeHead.ProcessPurgeCount = (nodeConfig != null && nodeConfig.HasAttribute("StepNo")) ? nodeConfig.Attributes["StepNo"].Value : "";
XmlNodeList stepNodeList = nodeModule.SelectNodes($"Step");
for (int i = 0; i < stepNodeList.Count; i++)
{
var recipeStep = new RecipeStep();
recipeData.Add(recipeStep);
XmlElement stepNode = stepNodeList[i] as XmlElement;
Dictionary<string, string> dic = new Dictionary<string, string>();
//遍历Step节点
foreach (XmlAttribute att in stepNode.Attributes)
{
if (recipeAllowedCommands.Contains(att.Name))
{
dic.Add(att.Name, att.Value);
}
}
//遍历Step子节点中所有的attribute属性节点
foreach (XmlElement subStepNode in stepNode.ChildNodes)
{
foreach (XmlAttribute att in subStepNode.Attributes)
{
if (recipeAllowedCommands.Contains(att.Name))
{
dic.Add(att.Name, att.Value);
}
}
//遍历Step子节点的子节点中所有的attribute属性节点
foreach (XmlElement subsubStepNode in subStepNode.ChildNodes)
{
foreach (XmlAttribute att in subsubStepNode.Attributes)
{
if (recipeAllowedCommands.Contains(att.Name))
{
dic.Add(att.Name, att.Value);
}
}
}
}
recipeStep.StepName = dic["Name"];
recipeStep.StepTime = Convert.ToDouble(dic["Time"]);
recipeStep.EndBy = EnumEndByCondition.ByTime;
if (dic.ContainsKey("Loop"))
{
string loopStr = dic["Loop"];
recipeStep.IsLoopStartStep = System.Text.RegularExpressions.Regex.Match(loopStr, @"Loop\x20\d+\s*$").Success;
recipeStep.IsLoopEndStep = System.Text.RegularExpressions.Regex.Match(loopStr, @"Loop End$").Success;
if (recipeStep.IsLoopStartStep)
recipeStep.LoopCount = Convert.ToInt32(loopStr.Replace("Loop", string.Empty));
else
recipeStep.LoopCount = 0;
}
//if (recipeData.Count >= 2)
//{
// RecipeStep dummyStep = GetDummyStep(recipeData[recipeData.Count - 2].RecipeCommands, dic, module);
// if (dummyStep.RecipeCommands.Count > 0)
// {
// dummyStep.IsDummyStep = true;
// dummyStep.StepTime = 1; // 默认1秒
// dummyStep.EndBy = EnumEndByCondition.ByTime;
// recipeData.Insert(recipeData.Count - 1, dummyStep);
// }
//}
#region Remove
dic.Remove("StepNo");
dic.Remove("Name");
dic.Remove("Time");
//Recipe Step去除Mfc30.Ramp
dic.Remove("Mfc30.Ramp");
HeaterControlMode PSUControlMode = (HeaterControlMode)Enum.Parse(typeof(HeaterControlMode), dic["TC1.SetHeaterMode"]);
HeaterControlMode SCRControlMode = (HeaterControlMode)Enum.Parse(typeof(HeaterControlMode), dic["TC2.SetHeaterMode2"]);
dic["TC1.SetHeaterMode"] = ((int)PSUControlMode).ToString();
dic["TC2.SetHeaterMode2"] = ((int)SCRControlMode).ToString();
//if (PSUControlMode == HeaterControlMode.Pyro)
//{
// dic.Remove("TC1.RecipeSetL1Ratio");
// dic.Remove("TC1.RecipeSetL2Ratio");
// dic.Remove("TC1.RecipeSetL3Ratio");
//}
//else if (PSUControlMode == HeaterControlMode.TC)
//{
// dic.Remove("TC1.RecipeSetL1Ratio");
// dic.Remove("TC1.RecipeSetL2Ratio");
// dic.Remove("TC1.RecipeSetL3Ratio");
// dic.Remove("TC1.SetL1TargetSP");
// dic.Remove("TC1.SetL3TargetSP");
//}
//Power模式不在移除TargetSP,也需要设置
//if (PSUControlMode == HeaterControlMode.Power)
//{
// dic.Remove("TC1.SetL1TargetSP");
// dic.Remove("TC1.SetL2TargetSP");
// dic.Remove("TC1.SetL3TargetSP");
//}
//if (SCRControlMode == HeaterControlMode.Pyro)
//{
// dic.Remove("TC2.RecipeSetL1Ratio");
// dic.Remove("TC2.RecipeSetL2Ratio");
// dic.Remove("TC2.RecipeSetL3Ratio");
//}
//else if (SCRControlMode == HeaterControlMode.TC)
//{
// dic.Remove("TC2.RecipeSetL1Ratio");
// dic.Remove("TC2.RecipeSetL2Ratio");
// dic.Remove("TC2.RecipeSetL3Ratio");
//}
if (SCRControlMode == HeaterControlMode.Power)
{
dic.Remove("TC2.SetL3TargetSP");
}
//if (dic["GRPurgeHCI"] == "Disable")
//{
// dic.Remove("Mfc30.Ramp");
//}
dic.Remove("SHTotalFlow");
dic.Remove("SHTotalFlowSplitRatio");
//dic.Remove("SiSourTotalFlow");
dic.Remove("SiSourSplitRatio");
//dic.Remove("CSourTotalFlow");
dic.Remove("CSourSplitRatio");
//dic.Remove("DopeTotalFlow");
dic.Remove("N2ActualFlow");
dic.Remove("DopeSplitRatio");
dic.Remove("SHSuppTotalFlow");
dic.Remove("SHInnerFlow");
dic.Remove("SHMidFlow");
dic.Remove("SHOutterFlow");
//dic.Remove("GRPurgeHCI");
//dic.Remove("TotalVentFlow");
#endregion
foreach (string key in dic.Keys)
recipeStep.RecipeCommands.Add(key, dic[key]);
}
}
catch (Exception ex)
{
LOG.Write(ex);
reason = $"Recipe file content not valid, {recipeFile}, {ex.Message}";
return false;
}
return true;
}
/// <summary>
/// Check Run Vent Purge Switch, Add Dummy Step
/// </summary>
/// <param name="previousStep"></param>
/// <param name="currentStep"></param>
/// <returns></returns>
private static RecipeStep GetDummyStep(Dictionary<string, string> previousStep, Dictionary<string, string> currentStep, string module)
{
RecipeStep step = new RecipeStep();
List<string> lstFlowMode = new List<string>() { FlowMode.Run.ToString(), FlowMode.Vent.ToString() };
bool isTotalVentFlowRunSwitch = false;
#region Flow Mode
string previousSiH4FlowMode = previousStep["SiH4.SetValve"];
string previousTCSFlowMode = previousStep["TCS.SetValve"];
string previousHClFlowMode = previousStep["HCl.SetValve"];
string previousC2H4FlowMode = previousStep["C2H4.SetValve"];
string previousN2FlowMode = previousStep["N2Dilution.SetValve"];
string previousN2HighFlowMode = previousStep["N2HighDoping.SetValve"];
string previousTMAFlowMode = previousStep["TMA.SetValve"];
string currentSiH4FlowMode = currentStep["SiH4.SetValve"];
string currentTCSFlowMode = currentStep["TCS.SetValve"];
string currentHClFlowMode = currentStep["HCl.SetValve"];
string currentC2H4FlowMode = currentStep["C2H4.SetValve"];
string currentN2FlowMode = currentStep["N2Dilution.SetValve"];
string currentN2HighFlowMode = currentStep["N2HighDoping.SetValve"];
string currentTMAFlowMode = currentStep["TMA.SetValve"];
#endregion
#region Check Si Source
double previousSiSourTotalFlow = Convert.ToDouble(previousStep["SiSourTotalFlow"]);
double previousTCSBubbHighFlow = Convert.ToDouble(previousStep["Mfc10.Ramp"]);
double previousTCSBubbLowFlow = Convert.ToDouble(previousStep["Mfc11.Ramp"]);
double previousTCSPushFlow = Convert.ToDouble(previousStep["Mfc12.Ramp"]);
double previousHClFlow = Convert.ToDouble(previousStep["Mfc13.Ramp"]);
double previousSiH4Flow = Convert.ToDouble(previousStep["Mfc14.Ramp"]);
double TCSFlow = 0;
double TCS_Eff = SC.GetValue<double>($"PM.{module}.Efficiency.TCS-Eff");
if (currentTCSFlowMode == FlowMode.Run.ToString() || currentTCSFlowMode == FlowMode.Vent.ToString())
TCSFlow = (previousTCSBubbHighFlow + previousTCSBubbLowFlow) * TCS_Eff + previousTCSPushFlow;
else if (currentTCSFlowMode == FlowMode.Purge.ToString())
TCSFlow = previousTCSBubbHighFlow + previousTCSBubbLowFlow + previousTCSPushFlow;
bool isSiSourRunVentSwitch = false;
if ((previousSiH4FlowMode != currentSiH4FlowMode) && lstFlowMode.Contains(previousSiH4FlowMode) && lstFlowMode.Contains(currentSiH4FlowMode))
{
isSiSourRunVentSwitch = true;
isTotalVentFlowRunSwitch = true;
step.RecipeCommands.Add("SiH4.SetValve", currentSiH4FlowMode);
}
if ((previousTCSFlowMode != currentTCSFlowMode) && lstFlowMode.Contains(previousTCSFlowMode) && lstFlowMode.Contains(currentTCSFlowMode))
{
isSiSourRunVentSwitch = true;
isTotalVentFlowRunSwitch = true;
step.RecipeCommands.Add("TCS.SetValve", currentTCSFlowMode);
}
if ((previousHClFlowMode != currentHClFlowMode) && lstFlowMode.Contains(previousHClFlowMode) && lstFlowMode.Contains(currentHClFlowMode))
{
isSiSourRunVentSwitch = true;
isTotalVentFlowRunSwitch = true;
step.RecipeCommands.Add("HCl.SetValve", currentHClFlowMode);
}
if (isSiSourRunVentSwitch)
{
double startM9Flow = previousSiSourTotalFlow - (currentSiH4FlowMode == FlowMode.Run.ToString() ? previousSiH4Flow : 0)
- (currentTCSFlowMode == FlowMode.Run.ToString() ? TCSFlow : 0) - (currentHClFlowMode == FlowMode.Run.ToString() ? previousHClFlow : 0);
step.RecipeCommands.Add("Mfc9.Ramp", startM9Flow.ToString("F1"));
}
#endregion
#region Check C Source
double previousCSourTotalFlow = Convert.ToDouble(previousStep["CSourTotalFlow"]);
double previousC2H4Flow = Convert.ToDouble(previousStep["Mfc16.Ramp"]);
bool isCSourRunVentSwitch = false;
if ((previousC2H4FlowMode != currentC2H4FlowMode) && lstFlowMode.Contains(previousC2H4FlowMode) && lstFlowMode.Contains(currentC2H4FlowMode))
{
isCSourRunVentSwitch = true;
isTotalVentFlowRunSwitch = true;
step.RecipeCommands.Add("C2H4.SetValve", currentC2H4FlowMode);
}
if (isCSourRunVentSwitch)
{
double startM15Flow = previousCSourTotalFlow - (currentC2H4FlowMode == FlowMode.Run.ToString() ? previousC2H4Flow : 0);
step.RecipeCommands.Add("Mfc15.Ramp", startM15Flow.ToString("F1"));
}
#endregion
#region Check Dope
double previousDopeTotalFlow = Convert.ToDouble(previousStep["DopeTotalFlow"]);
double previousN2HighFlow = Convert.ToDouble(previousStep["Mfc5.Ramp"]);
double previousDilutedN2Flow = Convert.ToDouble(previousStep["Mfc6.Ramp"]);
double previousTMABubbFlow = Convert.ToDouble(previousStep["Mfc7.Ramp"]);
double previousTMAPushFlow = Convert.ToDouble(previousStep["Mfc8.Ramp"]);
double TMAFlow = 0;
double TMA_Eff = SC.GetValue<double>($"PM.{module}.Efficiency.TMA-Eff");
if (currentTMAFlowMode == FlowMode.Run.ToString() || currentTMAFlowMode == FlowMode.Vent.ToString())
TMAFlow = previousTMABubbFlow * TMA_Eff + previousTMAPushFlow;
else if (currentTMAFlowMode == FlowMode.Purge.ToString())
TMAFlow = previousTMABubbFlow + previousTMAPushFlow;
bool isDopeRunVentSwitch = false;
if ((previousN2FlowMode != currentN2FlowMode) && lstFlowMode.Contains(previousN2FlowMode) && lstFlowMode.Contains(currentN2FlowMode))
{
isDopeRunVentSwitch = true;
isTotalVentFlowRunSwitch = true;
step.RecipeCommands.Add("N2Dilution.SetValve", currentN2FlowMode);
}
if ((previousN2HighFlowMode != currentN2HighFlowMode) && lstFlowMode.Contains(previousN2HighFlowMode) && lstFlowMode.Contains(currentN2HighFlowMode))
{
isDopeRunVentSwitch = true;
isTotalVentFlowRunSwitch = true;
step.RecipeCommands.Add("N2HighDoping.SetValve", currentN2HighFlowMode);
//#region M5 Control
//step.RecipeCommands.Add("Mfc5.SetMode", "Normal");
//step.RecipeCommands.Add("Mfc5.Ramp", (currentN2HighFlowMode == FlowMode.Run.ToString() ? currentN2HighFlow : 0).ToString("F1"));
//#endregion
}
if (previousTMAFlowMode != currentTMAFlowMode && lstFlowMode.Contains(previousTMAFlowMode) && lstFlowMode.Contains(currentTMAFlowMode))
{
isDopeRunVentSwitch = true;
isTotalVentFlowRunSwitch = true;
step.RecipeCommands.Add("TMA.SetValve", currentTMAFlowMode);
}
if (isDopeRunVentSwitch)
{
double startM2Flow = previousDopeTotalFlow - (currentTMAFlowMode == FlowMode.Run.ToString() ? TMAFlow : 0)
- (currentN2FlowMode == FlowMode.Run.ToString() ? previousDilutedN2Flow + (currentN2HighFlowMode == FlowMode.Run.ToString() ? previousN2HighFlow : 0) : 0);
step.RecipeCommands.Add("Mfc2.Ramp", startM2Flow.ToString("F1"));
}
#endregion
#region Check Total Vent Flow
double previousTotalVentFlow = Convert.ToDouble(previousStep["TotalVentFlow"]);
double currentN2LowFlow = Convert.ToDouble(currentStep["Mfc4.Ramp"]);
double currentDiluFlowForN2 = Convert.ToDouble(currentStep["Mfc3.Ramp"]);
double currentSiSourTotalFlowForPurge = (currentSiH4FlowMode != FlowMode.Run.ToString() ? previousSiH4Flow : 0)
+ (currentTCSFlowMode != FlowMode.Run.ToString() ? TCSFlow : 0) + (currentHClFlowMode != FlowMode.Run.ToString() ? previousHClFlow : 0);
double currentCSourTotalFlowForPurge = currentC2H4FlowMode != FlowMode.Run.ToString() ? previousC2H4Flow : 0;
double N2Flow = (currentN2FlowMode != FlowMode.Run.ToString() ? previousDilutedN2Flow : 0) + (currentN2HighFlowMode != FlowMode.Run.ToString() ? previousN2HighFlow : 0);
double currentDopeTotalFlowForPurge = N2Flow + (currentTMAFlowMode != FlowMode.Run.ToString() ? TMAFlow : 0);
if (isTotalVentFlowRunSwitch)
{
double startM1Flow = previousTotalVentFlow - currentSiSourTotalFlowForPurge - currentCSourTotalFlowForPurge - currentDopeTotalFlowForPurge;
step.RecipeCommands.Add("Mfc1.Ramp", startM1Flow.ToString("F1"));
}
#endregion
return step;
}
public static bool CheckDataAvalible(string recipeFile, string module, out string strErrorInfo)
{
strErrorInfo = "";
return true;
Dictionary<string, RecipeDataColumn> lstRecipeDataColumn = new Dictionary<string, RecipeDataColumn>();
//获取模板的Double类型,并读取最大值最小值
var str = RecipeFileManager.Instance.GetRecipeFormatXml("Sic//Process");//LoadRecipeFormat();
XmlDocument doc = new XmlDocument();
try
{
doc.LoadXml(str);
XmlNode nodeRoot = doc.SelectSingleNode("TableRecipeFormat");
}
catch (Exception ex)
{
LOG.Write(ex);
}
XmlNodeList nodes = doc.SelectNodes("TableRecipeFormat/Catalog/Group");
foreach (XmlNode node in nodes)
{
XmlNodeList childNodes = node.SelectNodes("Step");
foreach (XmlNode step in childNodes)
{
if (step.Attributes["InputType"].Value == "DoubleInput")
{
string recipeName = step.Attributes["ControlName"].Value;
if (recipeName == "Time")
{
continue;
}
//MFC和PC的最大值读取MFC的设定
string displayText = new Regex(@"\(M\d+\)").Match(step.Attributes["DisplayName"].Value).Value;
string maxConfig = $"PM.{module}.RecipeConfig.{strAbc(node.Attributes["DisplayName"].Value)}.{strAbc(step.Attributes["DisplayName"].Value)}.Max";
string minConfig = $"PM.{module}.RecipeConfig.{strAbc(node.Attributes["DisplayName"].Value)}.{strAbc(step.Attributes["DisplayName"].Value)}.Min";
if (displayText.Contains("M"))
{
maxConfig = $"PM.{module}.MFC.Mfc{displayText.Replace("(M", "").Replace(")", "")}.N2Scale";
}
else
{
displayText = new Regex(@"\(PC\d+\)").Match(step.Attributes["DisplayName"].Value).Value;
if (displayText.Contains("PC"))
{
maxConfig = $"PM.{module}.PC.PC{displayText.Replace("(PC", "").Replace(")", "")}.Scale";
}
}
double max = 0;// GetDoubleValueByName("PM", maxConfig);
double mix = 0;// GetDoubleValueByName("PM", minConfig);
if (displayText.Contains("M"))
{
max = GetDoubleValueByName("PM", maxConfig);
mix = max * GetDoubleValueByName("PM", $"PM.{module}.MFC.MinScale") / 100.0;
}
else
{
max = GetDoubleValueByName("PM", maxConfig);
mix = GetDoubleValueByName("PM", minConfig);
}
lstRecipeDataColumn.Add(recipeName, new RecipeDataColumn() { maxNum = max, minMum = mix, disPlay = step.Attributes["DisplayName"].Value });
}
}
}
string content = RecipeFileManager.Instance.LoadRecipe("", recipeFile, false);
if (string.IsNullOrEmpty(content))
{
return false;
}
//获取工艺程序文件中所有步的内容
XmlDocument rcpDataDoc = new XmlDocument();
rcpDataDoc.LoadXml(content);
XmlNode nodeModule = rcpDataDoc.SelectSingleNode($"/Aitex/TableRecipeData/Module[@Name='{module}']");
if (nodeModule == null)
{
return false;
}
XmlNodeList stepNodeList = nodeModule.SelectNodes($"Step");
for (int i = 0; i < stepNodeList.Count; i++)
{
XmlElement stepNode = stepNodeList[i] as XmlElement;
string stepNo = "";
foreach (XmlAttribute att in stepNode.Attributes)
{
//att.Name, att.Value
if (att.Name == "StepNo")
{
stepNo = att.Value;
}
if (lstRecipeDataColumn.ContainsKey(att.Name))
{
double cValue = 0;
double maxValue = lstRecipeDataColumn[att.Name].maxNum;
double minValue = lstRecipeDataColumn[att.Name].minMum;
string columnDisplay = lstRecipeDataColumn[att.Name].disPlay;
if (!double.TryParse(att.Value, out cValue))
{
strErrorInfo += "\r\n" + $"Step[{stepNo}]: Column [{columnDisplay}] value [{att.Value}] is incorrect numerical format";
}
else if (cValue > maxValue || cValue < minValue)
{
strErrorInfo += "\r\n" + $"Step {stepNo} {columnDisplay}: value {cValue} is out of range {minValue}-{maxValue}";
}
}
}
}
if (!String.IsNullOrEmpty(strErrorInfo))
{
return false;
}
return true;
}
/// <summary>
/// 去除小数点、箭头、括号(含括号里的内容)、横杠
/// </summary>
/// <param name="DisplayName"></param>
/// <returns></returns>
private static string strAbc(string DisplayName)
{
string displayname = DisplayName;
if (Regex.IsMatch(displayname.Substring(0, 1), @"^[+-]?\d*[.]?\d*$"))
{
displayname = displayname.Remove(0, displayname.IndexOf(".") + 1);//去除序号
}
displayname = displayname.Trim().Replace(" ", "").Replace(".", "").Replace("->", "_").Replace("-", "");
if (displayname.Contains("(") && displayname.Contains(")"))
{
displayname = displayname.Remove(displayname.IndexOf("("), displayname.IndexOf(")") - displayname.IndexOf("(") + 1);
}
return displayname;
}
private static double GetDoubleValueByName(string module, string name)
{
try
{
SCConfigItem item = SC.GetConfigItem(name);
if (item != null)
{
if (item.Type == "Integer")
{
return item.IntValue;
}
else if (item.Type == "Double")
{
return item.DoubleValue;
}
}
if (name.EndsWith(".Default"))
{
return 0;
}
else if (name.EndsWith(".Min"))
{
return 0;
}
else if (name.EndsWith(".Max"))
{
return 10000;
}
return 0;
}
catch
{
return 0;
}
}
}
}