Recipe.cs 21 KB

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