Recipe.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. using Aitex.Common.Util;
  2. using Aitex.Core.RT.Event;
  3. using Aitex.Core.RT.Log;
  4. using Aitex.Core.RT.SCCore;
  5. using EPInterface.Datas;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Xml;
  9. namespace VirgoRT.Modules
  10. {
  11. /// <summary>
  12. /// Recipe head
  13. /// </summary>
  14. public class RecipeHead
  15. {
  16. public string RecipeVariation { get; set; }
  17. public string CreationTime { get; set; }
  18. public string LastRevisionTime { get; set; }
  19. public string CreatedBy { get; set; }
  20. public string LastModifiedBy { get; set; }
  21. public string PressureMode { get; set; }
  22. public string Description { get; set; }
  23. public string Barcode { get; set; }
  24. public string BasePressure { get; set; }
  25. public string PumpDownLimit { get; set; }
  26. //public string ElectrodeTemp { get; set; }
  27. //public string HeaterTemp { get; set; }
  28. public string PurgeActive { get; set; }
  29. public string MatchPositionC1 { get; set; }
  30. public string MatchPositionC2 { get; set; }
  31. public string SubstrateTemp { get; set; }
  32. public string PumpingPinState { get; set; }
  33. public string NotToPurgeOrVent { get; set; } //For keep vacuum after idle clean
  34. public string VentingPinState { get; set; }
  35. public string PinDownPressure { get; set; }
  36. }
  37. public class RecipeStep
  38. {
  39. public string StepName;
  40. public double StepTime;
  41. public bool IsJumpStep;
  42. public bool IsLoopStartStep;
  43. public bool IsLoopEndStep;
  44. public int LoopCount;
  45. public string EndBy;
  46. public string EndPointConfig;
  47. public bool FaultIfNoEPDTrigger;
  48. //public double EndByValue;
  49. public Dictionary<string, string> RecipeCommands = new Dictionary<string, string>();
  50. public Dictionary<string, object[]> ToleranceCommands = new Dictionary<string, object[]>();
  51. }
  52. public class Recipe
  53. {
  54. /// <summary>
  55. /// 当前Recipe Run 记录对应的Guid
  56. /// 每个Recipe Run 都对应有一个唯一的Guid
  57. /// </summary>
  58. public static Guid CurrentRecipeRunGuid;
  59. /// <summary>
  60. /// 解析工艺程序文件
  61. /// </summary>
  62. /// <param name="recipeName">工艺程序名</param>
  63. /// <param name="xmlRecipeData">xml格式的工艺数据内容</param>
  64. /// <param name="recipeData">返回解析的工艺数据变量</param>
  65. /// <returns>True:解析成功 | False:解析失败</returns>
  66. public static bool Parse(string chamberId, string recipe, out RecipeHead recipeHead, out List<RecipeStep> recipeData)
  67. {
  68. recipeHead = new RecipeHead();
  69. recipeData = new List<RecipeStep>();
  70. try
  71. {
  72. //获取工艺程序文件中允许的命令字符串列表
  73. //目的:如果工艺程序文件中含有规定之外的命令,则被禁止执行
  74. HashSet<string> recipeAllowedCommands = new HashSet<string>();
  75. XmlDocument rcpFormatDoc = new XmlDocument();
  76. string recipeSchema = PathManager.GetCfgDir() + "RecipeFormat.xml";
  77. rcpFormatDoc.Load(recipeSchema);
  78. XmlNodeList rcpItemNodeList = rcpFormatDoc.SelectNodes("/Aitex/TableRecipeFormat/Catalog/Group/Step");
  79. foreach (XmlElement item in rcpItemNodeList)
  80. recipeAllowedCommands.Add(item.Attributes["ControlName"].Value);
  81. //获取工艺程序文件中所有步的内容
  82. XmlDocument rcpDataDoc = new XmlDocument();
  83. rcpDataDoc.LoadXml(recipe);
  84. recipeHead.PressureMode = rcpDataDoc.DocumentElement.HasAttribute("PressureMode") ? rcpDataDoc.DocumentElement.Attributes["PressureMode"].Value : "";
  85. recipeHead.BasePressure = rcpDataDoc.DocumentElement.HasAttribute("BasePressure") ? rcpDataDoc.DocumentElement.Attributes["BasePressure"].Value : "";
  86. recipeHead.PumpDownLimit = rcpDataDoc.DocumentElement.HasAttribute("PumpDownLimit") ? rcpDataDoc.DocumentElement.Attributes["PumpDownLimit"].Value : "";
  87. recipeHead.PurgeActive = rcpDataDoc.DocumentElement.HasAttribute("PurgeActive") ? rcpDataDoc.DocumentElement.Attributes["PurgeActive"].Value : "";
  88. recipeHead.MatchPositionC1 = rcpDataDoc.DocumentElement.HasAttribute("MatchPositionC1") ? rcpDataDoc.DocumentElement.Attributes["MatchPositionC1"].Value : "";
  89. recipeHead.MatchPositionC2 = rcpDataDoc.DocumentElement.HasAttribute("MatchPositionC2") ? rcpDataDoc.DocumentElement.Attributes["MatchPositionC2"].Value : "";
  90. recipeHead.Barcode = rcpDataDoc.DocumentElement.HasAttribute("Barcode") ? rcpDataDoc.DocumentElement.Attributes["Barcode"].Value : "";
  91. recipeHead.SubstrateTemp = rcpDataDoc.DocumentElement.HasAttribute("SubstrateTemp") ? rcpDataDoc.DocumentElement.Attributes["SubstrateTemp"].Value : "";
  92. recipeHead.PumpingPinState = rcpDataDoc.DocumentElement.HasAttribute("PumpingPinState") ? rcpDataDoc.DocumentElement.Attributes["PumpingPinState"].Value : "Down";
  93. //For keep vacuum after idle clean
  94. recipeHead.NotToPurgeOrVent = rcpDataDoc.DocumentElement.HasAttribute("NotToPurgeOrVent") ? rcpDataDoc.DocumentElement.Attributes["NotToPurgeOrVent"].Value : "";
  95. recipeHead.VentingPinState = rcpDataDoc.DocumentElement.HasAttribute("VentingPinState") ? rcpDataDoc.DocumentElement.Attributes["VentingPinState"].Value : "Down";
  96. recipeHead.PinDownPressure = rcpDataDoc.DocumentElement.HasAttribute("PinDownPressure") ? rcpDataDoc.DocumentElement.Attributes["PinDownPressure"].Value : "1000";
  97. XmlNodeList stepNodeList = rcpDataDoc.SelectNodes("/TableRecipeData/Step");
  98. for (int i = 0; i < stepNodeList.Count; i++)
  99. {
  100. var recipeStep = new RecipeStep();
  101. recipeData.Add(recipeStep);
  102. XmlElement stepNode = stepNodeList[i] as XmlElement;
  103. Dictionary<string, string> dic = new Dictionary<string, string>();
  104. //遍历Step节点
  105. foreach (XmlAttribute att in stepNode.Attributes)
  106. {
  107. if (recipeAllowedCommands.Contains(att.Name))
  108. {
  109. dic.Add(att.Name, att.Value);
  110. }
  111. }
  112. //遍历Step子节点中所有的attribute属性节点
  113. foreach (XmlElement subStepNode in stepNode.ChildNodes)
  114. {
  115. foreach (XmlAttribute att in subStepNode.Attributes)
  116. {
  117. if (recipeAllowedCommands.Contains(att.Name))
  118. {
  119. dic.Add(att.Name, att.Value);
  120. }
  121. }
  122. //遍历Step子节点的子节点中所有的attribute属性节点
  123. foreach (XmlElement subsubStepNode in subStepNode.ChildNodes)
  124. {
  125. foreach (XmlAttribute att in subsubStepNode.Attributes)
  126. {
  127. if (recipeAllowedCommands.Contains(att.Name))
  128. {
  129. dic.Add(att.Name, att.Value);
  130. }
  131. }
  132. }
  133. }
  134. recipeStep.IsJumpStep = true;//!Convert.ToBoolean(dic["Ramp"]);
  135. recipeStep.StepName = dic["Name"];
  136. recipeStep.StepTime = double.Parse(dic["Time"]);
  137. string loopStr = dic["Loop"];
  138. recipeStep.IsLoopStartStep = System.Text.RegularExpressions.Regex.Match(loopStr, @"Loop\x20\d+\s*$").Success;
  139. recipeStep.IsLoopEndStep = System.Text.RegularExpressions.Regex.Match(loopStr, @"Loop End$").Success;
  140. if (recipeStep.IsLoopStartStep)
  141. recipeStep.LoopCount = Convert.ToInt32(loopStr.Replace("Loop", string.Empty));
  142. else
  143. recipeStep.LoopCount = 0;
  144. //recipeStep.EndByValue = Convert.ToDouble(dic["EndValue"]);
  145. recipeStep.EndBy = dic["EndBy"];
  146. if (recipeStep.EndBy == "EndByRfTime")
  147. {
  148. recipeStep.StepTime = double.Parse(dic["Rf.SetPowerOnTime"]);
  149. if (recipeStep.StepTime <= 0)
  150. {
  151. LOG.Error("recipe 没有定义RF Power on的时间");
  152. return false;
  153. }
  154. }
  155. int rfPower = (int)Convert.ToDouble(dic["Rf.SetPower"]);
  156. dic.Add("Rf.SetPowerOnOff", rfPower > 0 ? "true" : "false");
  157. if (SC.GetValue<bool>($"{chamberId}.BiasRf.EnableBiasRF"))
  158. {
  159. int rfPowerBias = (int)Convert.ToDouble(dic["BiasRf.SetPower"]);
  160. dic.Add("BiasRf.SetPowerOnOff", rfPowerBias > 0 ? "true" : "false");
  161. if(SC.GetValue<bool>($"{chamberId}.BiasMatch.EnableBiasMatch"))
  162. {
  163. dic["BiasMatch.SetMatchProcessMode"] = dic["BiasRf.SetMatchProcessMode"];
  164. if (SC.GetValue<int>($"{chamberId}.BiasMatch.MFG") == (int)MatchMFG.Revtech)
  165. {
  166. dic["BiasMatch.SetMatchPositionC1"] = dic["BiasRf.SetMatchPositionC1"];
  167. dic["BiasMatch.SetMatchPositionC2"] = dic["BiasRf.SetMatchPositionC2"];
  168. }
  169. if (SC.GetValue<int>($"{chamberId}.BiasMatch.MFG") == (int)MatchMFG.AdTec)
  170. {
  171. if (SC.GetValue<int>($"{chamberId}.BiasMatch.AdTecBiasMatchModel") == 0)
  172. {
  173. dic["BiasMatch.SetMatchPositionC1"] = dic["BiasRf.SetMatchPositionC1"];
  174. dic["BiasMatch.SetMatchPositionC2"] = dic["BiasRf.SetMatchPositionC2"];
  175. }
  176. if (SC.GetValue<int>($"{chamberId}.BiasMatch.AdTecBiasMatchModel") == 1)
  177. {
  178. dic["BiasMatch.SetMatchPositionC1AndC2"] = dic["BiasRf.SetMatchPositionC1"] + "_" + dic["BiasRf.SetMatchPositionC2"];
  179. }
  180. }
  181. }
  182. // RS232 AdTec match
  183. if (SC.GetValue<int>($"{chamberId}.match.CommunicationType") == (int)CommunicationType.RS232 &&
  184. SC.GetValue<int>($"{chamberId}.match.MFG") == (int)MatchMFG.AdTec)
  185. {
  186. //BiasRf1.SetMatchProcessMode
  187. dic["match.SetMatchProcessMode"] = dic["BiasRf.SetMatchProcessMode"];
  188. dic.Remove("BiasRf.SetMatchProcessMode");
  189. dic["match.SetMatchPositionC1"] = dic["BiasRf.SetMatchPositionC1"];
  190. dic.Remove("BiasRf.SetMatchPositionC1");
  191. dic["match.SetMatchPositionC2"] = dic["BiasRf.SetMatchPositionC2"];
  192. dic.Remove("BiasRf.SetMatchPositionC2");
  193. if (SC.GetValue<int>($"{chamberId}.match.AdTecSourceMatchModel") == 1)
  194. {
  195. dic["match.SetMatchPositionC1AndC2"] = dic["match.SetMatchPositionC1"] + "_" + dic["match.SetMatchPositionC2"];
  196. dic.Remove("match.SetMatchPositionC1");
  197. dic.Remove("match.SetMatchPositionC2");
  198. }
  199. }
  200. }
  201. else
  202. {
  203. dic.Remove("BiasRf.SetPower");
  204. dic.Remove("BiasRf.SetMatchProcessMode");
  205. dic.Remove("BiasRf.SetMatchPositionC1");
  206. dic.Remove("BiasRf.SetMatchPositionC2");
  207. }
  208. bool epdInstalled = SC.ContainsItem("System.SetUp.EPDInstalled") && SC.GetValue<bool>($"System.SetUp.EPDInstalled");
  209. if (!epdInstalled)
  210. {
  211. if (dic.ContainsKey("EPD.SetConfig"))
  212. dic.Remove("EPD.SetConfig");
  213. }
  214. else
  215. {
  216. recipeStep.EndPointConfig = dic.ContainsKey("EPD.SetConfig") ? dic["EPD.SetConfig"] : null;
  217. if (string.IsNullOrEmpty(recipeStep.EndPointConfig))
  218. {
  219. if (recipeStep.EndBy == "EndByEndPoint")
  220. {
  221. EV.PostWarningLog("System", "EndPoint is empty");
  222. return false;
  223. }
  224. recipeStep.EndPointConfig = SC.GetStringValue("System.EndPoint.EndPointDefaultValue");
  225. }
  226. else
  227. {
  228. if (!ParseEPD(recipeStep.EndPointConfig, out EPDConfig config))
  229. {
  230. EV.PostWarningLog("System", "EndPoint config is not valid");
  231. return false;
  232. }
  233. recipeStep.FaultIfNoEPDTrigger = config.FaultIfNoEPDTrigger;
  234. }
  235. }
  236. //tolerance
  237. List<string> items = new List<string>() { "MfcGas1", "MfcGas2", "MfcGas3", "MfcGas4", "MfcGas5", "PressureControl", "Rf", "BiasRf" };
  238. Dictionary<string, object[]> tolerance = new Dictionary<string, object[]>();
  239. foreach (var item in items)
  240. {
  241. if (item == "BiasRf" && !SC.GetValue<bool>($"{chamberId}.BiasRf.EnableBiasRF"))
  242. {
  243. dic.Remove(($"{item}.SoftTolerance"));
  244. dic.Remove(($"{item}.HardTolerance"));
  245. continue;
  246. }
  247. var time = SC.GetValue<int>($"{chamberId}.RecipeToleranceIgnoreTime");
  248. var warning = dic.ContainsKey($"{item}.SoftTolerance") ? dic[$"{item}.SoftTolerance"] : "0";
  249. var alarm = dic.ContainsKey($"{item}.HardTolerance") ? dic[$"{item}.HardTolerance"] : "0";
  250. tolerance[$"{item}.SetRecipeTolerance"] = new object[]
  251. {
  252. time, warning, alarm
  253. };
  254. dic.Remove(($"{item}.SoftTolerance"));
  255. dic.Remove(($"{item}.HardTolerance"));
  256. }
  257. recipeStep.ToleranceCommands = tolerance;
  258. //dic.Remove("Ramp");
  259. dic.Remove("StepNo");
  260. dic.Remove("Name");
  261. dic.Remove("Loop");
  262. dic.Remove("Time");
  263. dic.Remove("EndBy");
  264. //dic.Remove("EndValue");
  265. dic.Remove("Rf.SetPowerOnTime");
  266. foreach (string key in dic.Keys)
  267. recipeStep.RecipeCommands.Add(key, dic[key]);
  268. }
  269. }
  270. catch (Exception ex)
  271. {
  272. LOG.Write(ex);
  273. return false;
  274. }
  275. return true;
  276. }
  277. private static bool ParseEPD(string config, out EPDConfig epdConfig)
  278. {
  279. epdConfig = new EPDConfig();
  280. try
  281. {
  282. epdConfig.nParameterCount = 1;
  283. string[] items = config.Split(';');
  284. foreach (var item in items)
  285. {
  286. if (string.IsNullOrEmpty(item))
  287. continue;
  288. string[] pairs = item.Split('=');
  289. if (pairs.Length != 2)
  290. continue;
  291. switch (pairs[0])
  292. {
  293. case "ExposureTime":
  294. epdConfig.Columns[0].nCCDExposureTime = int.Parse(pairs[1]);
  295. break;
  296. case "WaveLengthA":
  297. epdConfig.Columns[0].nWaveLength[0] = ushort.Parse(pairs[1]);
  298. break;
  299. case "BinningA":
  300. epdConfig.Columns[0].nBinning[0] = ushort.Parse(pairs[1]);
  301. break;
  302. case "WaveLengthB":
  303. epdConfig.Columns[0].nWaveLength[1] = ushort.Parse(pairs[1]);
  304. break;
  305. case "BinningB":
  306. epdConfig.Columns[0].nBinning[1] = ushort.Parse(pairs[1]);
  307. break;
  308. case "WaveLengthC":
  309. epdConfig.Columns[0].nWaveLength[2] = ushort.Parse(pairs[1]);
  310. break;
  311. case "BinningC":
  312. epdConfig.Columns[0].nBinning[2] = ushort.Parse(pairs[1]);
  313. break;
  314. case "WaveLengthD":
  315. epdConfig.Columns[0].nWaveLength[3] = ushort.Parse(pairs[1]);
  316. break;
  317. case "BinningD":
  318. epdConfig.Columns[0].nBinning[3] = ushort.Parse(pairs[1]);
  319. break;
  320. case "Fd":
  321. epdConfig.Columns[0].cFunc = pairs[1];
  322. break;
  323. case "PrefilterTime":
  324. epdConfig.Columns[0].nPreFilterTime = int.Parse(pairs[1]);
  325. break;
  326. case "PostfilterTime":
  327. epdConfig.Columns[0].nPostFilterTime = int.Parse(pairs[1]);
  328. break;
  329. case "AlgorithmType":
  330. epdConfig.Columns[0].algorithmType = MapType(pairs[1]);
  331. break;
  332. case "Criteria":
  333. epdConfig.Columns[0].nCriteria = float.Parse(pairs[1]);
  334. break;
  335. case "DelayTime":
  336. epdConfig.Columns[0].nDelayTime = int.Parse(pairs[1]);
  337. break;
  338. case "ValidationTime":
  339. epdConfig.Columns[0].nValidationTime = int.Parse(pairs[1]);
  340. break;
  341. case "ValidationValue":
  342. epdConfig.Columns[0].nValidationValue = int.Parse(pairs[1]);
  343. break;
  344. case "TimeWindow":
  345. epdConfig.Columns[0].nTimeWindow = int.Parse(pairs[1]);
  346. break;
  347. case "MinimalTime":
  348. epdConfig.Columns[0].nMinimalTime = int.Parse(pairs[1]);
  349. break;
  350. case "PostponeTime":
  351. epdConfig.Columns[0].nPostponeTime = int.Parse(pairs[1]);
  352. break;
  353. case "Control":
  354. epdConfig.Columns[0].bControl = Convert.ToBoolean(pairs[1]);
  355. break;
  356. case "Normalization":
  357. epdConfig.Columns[0].bNormalization = Convert.ToBoolean(pairs[1]);
  358. break;
  359. case "EnablePostponePercent":
  360. epdConfig.Columns[0].bPostponePercent = Convert.ToBoolean(pairs[1]);
  361. break;
  362. case "EnableCriterialPercent":
  363. epdConfig.Columns[0].bCriteriaPercent = Convert.ToBoolean(pairs[1]);
  364. break;
  365. case "EnableEventTrigger":
  366. epdConfig.Columns[0].bEvtTrigger = Convert.ToBoolean(pairs[1]);
  367. break;
  368. case "IsFaultIfNoTrigger":
  369. epdConfig.FaultIfNoEPDTrigger = Convert.ToBoolean(pairs[1]);
  370. break;
  371. }
  372. }
  373. return true;
  374. }
  375. catch (Exception ex)
  376. {
  377. LOG.Write(ex);
  378. EV.PostMessage("System", EventEnum.DefaultAlarm, "EPD config input not valid, ", ex.Message);
  379. return false;
  380. }
  381. }
  382. private static AlgorithmType MapType(string type)
  383. {
  384. switch (type)
  385. {
  386. case "Unknown": return AlgorithmType.ALG_NONE;
  387. case "Above_ABS_Value": return AlgorithmType.ALG_RISE_VALUE;
  388. case "Below_ABS_Value": return AlgorithmType.ALG_FALL_VALUE;
  389. case "Drop_Percent": return AlgorithmType.ALG_FALL_PERCENT;
  390. case "Up_Percent": return AlgorithmType.ALG_RISE_PERCENT;
  391. case "Range_In": return AlgorithmType.ALG_RANGE_IN;
  392. case "Gradient": return AlgorithmType.ALG_GRADIENT;
  393. case "Peek": return AlgorithmType.ALG_PEAK;
  394. case "Valley": return AlgorithmType.ALG_VALLEY;
  395. case "Min_Drop_Percent": return AlgorithmType.ALG_MIN_FALL_PERCENT;
  396. case "Min_Up_Percent": return AlgorithmType.ALG_MIN_RISE_PERCENT;
  397. case "Max_Drop_Percent": return AlgorithmType.ALG_MAX_FALL_PERCENT;
  398. case "Max_Up_Percent": return AlgorithmType.ALG_MAX_RISE_PERCENT;
  399. case "Rise_Fall": return AlgorithmType.ALG_RISE_FALL;
  400. case "Fall_Rise": return AlgorithmType.ALG_FALL_RISE;
  401. }
  402. return AlgorithmType.ALG_NONE;
  403. }
  404. }
  405. }