ReservoirRunRecipeRoutine.cs 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  1. using Aitex.Core.RT.Device;
  2. using Aitex.Core.RT.Log;
  3. using Aitex.Core.RT.Routine;
  4. using Aitex.Core.RT.SCCore;
  5. using Aitex.Core.Util;
  6. using CyberX8_Core;
  7. using CyberX8_RT.Devices.Facilities;
  8. using CyberX8_RT.Devices.Metal;
  9. using CyberX8_RT.Devices.PowerSupplier;
  10. using CyberX8_RT.Devices.Reservoir;
  11. using CyberX8_RT.Devices.Temperature;
  12. using CyberX8_RT.Modules.Metal;
  13. using CyberX8_RT.Modules;
  14. using MECF.Framework.Common.Alarm;
  15. using MECF.Framework.Common.CommonData;
  16. using MECF.Framework.Common.CommonData.Metal;
  17. using MECF.Framework.Common.CommonData.PowerSupplier;
  18. using MECF.Framework.Common.Persistent.Reservoirs;
  19. using MECF.Framework.Common.RecipeCenter;
  20. using MECF.Framework.Common.Routine;
  21. using MECF.Framework.Common.ToolLayout;
  22. using System;
  23. using System.Collections.Generic;
  24. using System.Diagnostics;
  25. namespace CyberX8_RT.Modules.Metal
  26. {
  27. public class ReservoirRunRecipeRoutine : RoutineBase, IRoutine
  28. {
  29. #region 常量
  30. private const int LOTTRACK_TIME = 1000;
  31. private const string CDA_1_PRESSURE_VALUE = "CDA1Pressure";
  32. private const string CDA_2_PRESSURE_VALUE = "CDA2Pressure";
  33. private const string STRATUS = "Stratus";
  34. #endregion
  35. private enum RecipeStep
  36. {
  37. PlatingDelay,
  38. PlatingDelayCheck,
  39. HotPlating,
  40. WaitPlatingDelay,
  41. RunPowerStep,
  42. RunPowerStepWait,
  43. LoopStart,
  44. LoopCheckRun,
  45. LoopEnd,
  46. StopLinmot,
  47. StopPowerSuppliers,
  48. SwitchToNormal,
  49. WaferHolderUnclampOn,
  50. End
  51. }
  52. #region 常量
  53. private const string SIDE_A = "SideA";
  54. private const string SIDE_B = "SideB";
  55. #endregion
  56. #region 内部变量
  57. /// <summary>
  58. /// recipe
  59. /// </summary>
  60. private DepRecipe _recipe;
  61. /// <summary>
  62. /// Plate Delay时间
  63. /// </summary>
  64. private DateTime _platingDelayTime = DateTime.Now;
  65. /// <summary>
  66. /// 启动步骤时间
  67. /// </summary>
  68. private DateTime _startStepTime = DateTime.Now;
  69. /// <summary>
  70. /// 电量步骤计数
  71. /// </summary>
  72. private Stopwatch _stepWatch = new Stopwatch();
  73. /// <summary>
  74. /// Power 集合
  75. /// </summary>
  76. List<PowerSupplierStepPeriodData> _powerSupplierStepPeriodDatas = new List<PowerSupplierStepPeriodData>();
  77. /// <summary>
  78. /// 单面
  79. /// </summary>
  80. private string _side;
  81. /// <summary>
  82. /// 步骤索引
  83. /// </summary>
  84. private int _stepIndex = 0;
  85. /// <summary>
  86. /// 设备
  87. /// </summary>
  88. private MetalCellDevice _device;
  89. /// <summary>
  90. /// 设备Entity
  91. /// </summary>
  92. private MetalEntity _metalEntity;
  93. /// <summary>
  94. /// A面电量
  95. /// </summary>
  96. private double _anodeAUsage;
  97. /// <summary>
  98. /// B面电量
  99. /// </summary>
  100. private double _anodeBUsage;
  101. /// <summary>
  102. /// 是否启动recipe步骤
  103. /// </summary>
  104. private bool _startRecipeStep = false;
  105. /// <summary>
  106. /// lock track time
  107. /// </summary>
  108. private DateTime _lotTackTime = DateTime.Now;
  109. /// <summary>
  110. /// LotTrack数据
  111. /// </summary>
  112. private List<MetalLotTrackData> _datas = new List<MetalLotTrackData>();
  113. /// <summary>
  114. /// LotTrack文件头数据
  115. /// </summary>
  116. private LotTrackFileHeaderCommonData _header = new LotTrackFileHeaderCommonData();
  117. /// <summary>
  118. /// Facilities
  119. /// </summary>
  120. private SystemFacilities _facilities;
  121. /// <summary>
  122. /// StandardHot Reservoir Device
  123. /// </summary>
  124. private StandardHotReservoirDevice _standardHotReservoirDevice;
  125. /// <summary>
  126. /// StandardHot Metal Device
  127. /// </summary>
  128. private StandardHotMetalDevice _standardHotMetalDevice;
  129. /// <summary>
  130. /// CompactMembran Reservoir Device
  131. /// </summary>
  132. private CompactMembranReservoirDevice _compactMembranReservoirDevice;
  133. /// <summary>
  134. /// CompactMembran Reservoir Metal
  135. /// </summary>
  136. private CompactMembranMetalDevice _compactMembranMetalDevice;
  137. /// <summary>
  138. /// MetalType
  139. /// </summary>
  140. private string _metalType;
  141. /// <summary>
  142. /// TC device
  143. /// </summary>
  144. private TemperatureController _temperatureController;
  145. /// <summary>
  146. /// Persistent value
  147. /// </summary>
  148. private MetalPersistentValue _persistentValue;
  149. /// <summary>
  150. /// PlatingDelay计时
  151. /// </summary>
  152. private DateTime _hotPlatingRunTime;
  153. /// <summary>
  154. /// 是否处于warning状态
  155. /// </summary>
  156. private bool _isVotlageWarningA = false;
  157. private bool _isVotlageWarningB = false;
  158. private bool _isCurrentWarningA = false;
  159. private bool _isCurrentWarningB = false;
  160. private bool _isZeroCurrent = false;
  161. private int _totalMicrosecond = 0;
  162. private double _currentHoldoffTime;
  163. private double _voltageHoldoffTime;
  164. private double _cellFlowHoldoffTime;
  165. private HoldoffTimeSignalMonitor _currentHoldoffTimeSignalMonitor;
  166. private HoldoffTimeSignalMonitor _voltageHoldoffTimeSignalMonitor;
  167. private HoldoffTimeSignalMonitor _cellFlowHoldoffTimeSignalMonitor;
  168. #endregion
  169. #region 属性
  170. /// <summary>
  171. /// A面电量
  172. /// </summary>
  173. public double AnodeAUsage { get { return _anodeAUsage; } }
  174. /// <summary>
  175. /// B面电量
  176. /// </summary>
  177. public double AnodeBUsage { get { return _anodeBUsage; } }
  178. /// <summary>
  179. /// LotTrack数据
  180. /// </summary>
  181. public List<MetalLotTrackData> MetalLotTrackDatas { get { return _datas; } }
  182. /// <summary>
  183. /// LotTrack文件头数据
  184. /// </summary>
  185. public LotTrackFileHeaderCommonData MetalLotTrackHeaderDatas { get { return _header; } }
  186. #endregion
  187. /// <summary>
  188. /// 构造函数
  189. /// </summary>
  190. public ReservoirRunRecipeRoutine(string moduleName) : base(moduleName)
  191. {
  192. _currentHoldoffTime = SC.GetValue<double>($"Metal.CurrentAlarmHoldoffTime");
  193. _voltageHoldoffTime = SC.GetValue<double>($"Metal.VoltageAlarmHoldoffTime");
  194. _cellFlowHoldoffTime = SC.GetValue<double>($"Metal.CellFlowAlarmHoldoffTime");
  195. _currentHoldoffTimeSignalMonitor = new HoldoffTimeSignalMonitor(Module, "Current");
  196. _voltageHoldoffTimeSignalMonitor = new HoldoffTimeSignalMonitor(Module, "Voltage");
  197. _cellFlowHoldoffTimeSignalMonitor = new HoldoffTimeSignalMonitor(Module, "CellFlow");
  198. }
  199. /// <summary>
  200. /// 中止
  201. /// </summary>
  202. public void Abort()
  203. {
  204. if (_device != null)
  205. {
  206. _device.SideAPowerSupplier.DisableOperation("", null);
  207. _device.SideBPowerSupplier.DisableOperation("", null);
  208. _device.SideAPowerSupplier.SwitchPowerRunModel((int)PowerRunModelEnum.Normal);
  209. _device.SideBPowerSupplier.SwitchPowerRunModel((int)PowerRunModelEnum.Normal);
  210. if (_device.IsLinmotMotorOn)
  211. {
  212. _device.StopLinmot();
  213. }
  214. }
  215. Runner.Stop("Manual Abort");
  216. }
  217. /// <summary>
  218. /// 监控
  219. /// </summary>
  220. /// <returns></returns>
  221. public RState Monitor()
  222. {
  223. LottrackRecord();
  224. Runner.Run(RecipeStep.PlatingDelay, PlatingDelay, _delay_1ms)
  225. .WaitWithStopCondition(RecipeStep.PlatingDelayCheck, () => { return _device.CheckLinmotRoutineEnd(); }, () => { return _device.CheckLinmotRoutineError(); })
  226. .RunIf(RecipeStep.HotPlating, _recipe.HotPlatingCurrentOn, HotPlating, _delay_1ms)
  227. .RunIf(RecipeStep.WaitPlatingDelay, !_recipe.HotPlatingCurrentOn, NullFun, WaitPlatingDelay, _recipe.PlatingDelaySeconds * 1000 + 10000)
  228. .RunIf(RecipeStep.RunPowerStep, !_recipe.HotPlatingCurrentOn, StartPowerStep, _delay_1ms)
  229. .WaitWithStopCondition(RecipeStep.RunPowerStepWait, CheckRecipeStepEndStatus, CheckRecipeStepStopStatus, _delay_5s)
  230. .LoopStart(RecipeStep.LoopStart, "Loop update linmot speed", _powerSupplierStepPeriodDatas.Count, NullFun, _delay_1ms)
  231. .LoopRunWithStopStatus(RecipeStep.LoopCheckRun, CheckStepComplete, () => { return false; }, 24 * 60 * 60 * 1000)
  232. .LoopEnd(RecipeStep.LoopEnd, NullFun, _delay_1ms)
  233. .Run(RecipeStep.StopLinmot, StopLinmot, _delay_1ms)
  234. .Run(RecipeStep.StopPowerSuppliers, StopPowerSupplier, _delay_1ms)
  235. .Run(RecipeStep.SwitchToNormal, SwitchToNormal, _delay_1ms)
  236. .End(RecipeStep.End, NullFun, _delay_1ms);
  237. return Runner.Status;
  238. }
  239. /// <summary>
  240. /// 记录Lottrack
  241. /// </summary>
  242. private void LottrackRecord()
  243. {
  244. //记录Lottrack
  245. if (DateTime.Now.Subtract(_lotTackTime).TotalMilliseconds >= LOTTRACK_TIME)
  246. {
  247. AddLotTrackData();
  248. _lotTackTime = DateTime.Now;
  249. }
  250. }
  251. /// <summary>
  252. /// 获取Lot Track数据
  253. /// </summary>
  254. /// <returns></returns>
  255. public void AddLotTrackData()
  256. {
  257. MetalLotTrackData data = new MetalLotTrackData();
  258. if (_metalType == STRATUS)
  259. {
  260. data.Flow = _standardHotMetalDevice.MetalDeviceData.CellFlow;
  261. data.ANLevel = 0;
  262. data.CALevel = _standardHotReservoirDevice.ReservoirData.Level;
  263. data.CAPumpSpeed = 0;
  264. }
  265. else
  266. {
  267. data.Flow = _compactMembranMetalDevice.MetalDeviceData.CellFlow;
  268. data.ANLevel = _compactMembranReservoirDevice.ReservoirData.ANLevel;
  269. data.CALevel = _compactMembranReservoirDevice.ReservoirData.CALevel;
  270. data.CAPumpSpeed = _compactMembranReservoirDevice.ReservoirData.CAPumpSpeed;
  271. }
  272. data.RunTime = 0;
  273. data.ClampCycleEngaged = _recipe.CycleClampsEnable;
  274. data.Temperature = _temperatureController.TemperatureData.ReserviorTemperature;
  275. data.TimeStamp = DateTime.Now;
  276. data.PowerSupplyA = _device.SideAPowerSupplier.Name;
  277. data.PowerSupplyB = _device.SideBPowerSupplier.Name;
  278. data.PosVoltageA = _device.SideAPowerSupplier.PowerSupplierData.Voltage;
  279. data.PosCurrentA = _device.SideAPowerSupplier.PowerSupplierData.Current;
  280. data.PosVoltageB = _device.SideBPowerSupplier.PowerSupplierData.Voltage;
  281. data.PosCurrentB = _device.SideBPowerSupplier.PowerSupplierData.Current;
  282. data.CDA_1_Pressure = _facilities.GetCommonLimitDataByName(CDA_1_PRESSURE_VALUE).Value;
  283. data.CDA_2_Pressure = _facilities.GetCommonLimitDataByName(CDA_2_PRESSURE_VALUE).Value;
  284. int maxStep = _powerSupplierStepPeriodDatas.Count;
  285. if (_stepIndex < maxStep)
  286. {
  287. data.StepNum = _stepIndex + 1;
  288. int length = _powerSupplierStepPeriodDatas[_stepIndex].Hour * 3600 + _powerSupplierStepPeriodDatas[_stepIndex].Minute * 60 +
  289. _powerSupplierStepPeriodDatas[_stepIndex].Second;
  290. data.DurationRef = length;
  291. data.ShearPlateSpeed = _recipe.CurrentRampProfileSteps[_stepIndex].ShearPlateSpeed;
  292. data.CurrentSP = _recipe.CurrentRampProfileSteps[_stepIndex].ForwardAmps;
  293. }
  294. else
  295. {
  296. data.StepNum = maxStep;
  297. int length = _powerSupplierStepPeriodDatas[maxStep - 1].Hour * 3600 + _powerSupplierStepPeriodDatas[maxStep - 1].Minute * 60 +
  298. _powerSupplierStepPeriodDatas[maxStep - 1].Second;
  299. data.DurationRef = length;
  300. data.ShearPlateSpeed = _recipe.CurrentRampProfileSteps[maxStep - 1].ShearPlateSpeed;
  301. data.CurrentSP = _recipe.CurrentRampProfileSteps[maxStep - 1].ForwardAmps;
  302. }
  303. RecipeStep step = (RecipeStep)Runner.CurrentStep;
  304. if (step <= RecipeStep.WaitPlatingDelay)
  305. {
  306. if (!_recipe.HotPlatingCurrentOn)
  307. {
  308. data.RunTime = DateTime.Now.Subtract(_hotPlatingRunTime).TotalSeconds;
  309. data.DurationRef = _recipe.PlatingDelaySeconds;
  310. data.StepNum = 0;
  311. }
  312. else
  313. {
  314. data.RunTime = 0;
  315. data.DurationRef = _recipe.PlatingDelaySeconds;
  316. data.StepNum = 0;
  317. }
  318. }
  319. else
  320. {
  321. if (_recipe.HotPlatingCurrentOn)
  322. {
  323. if (DateTime.Now.Subtract(_hotPlatingRunTime).TotalSeconds <= _recipe.PlatingDelaySeconds)
  324. {
  325. data.RunTime = DateTime.Now.Subtract(_hotPlatingRunTime).TotalSeconds;
  326. data.DurationRef = _recipe.PlatingDelaySeconds;
  327. data.StepNum = 0;
  328. }
  329. else
  330. {
  331. if (data.StepNum == 1) data.DurationRef -= _recipe.PlatingDelaySeconds;
  332. data.RunTime = (data.StepNum == 1) ? (DateTime.Now.Subtract(_startStepTime).TotalSeconds - _recipe.PlatingDelaySeconds) : DateTime.Now.Subtract(_startStepTime).TotalSeconds;
  333. }
  334. }
  335. else
  336. {
  337. data.RunTime = DateTime.Now.Subtract(_startStepTime).TotalSeconds;
  338. }
  339. }
  340. _datas.Add(data);
  341. }
  342. /// <summary>
  343. /// Plate Delay
  344. /// </summary>
  345. /// <returns></returns>
  346. private bool PlatingDelay()
  347. {
  348. double speed = _recipe.CurrentRampProfileSteps[0].ShearPlateSpeed;
  349. bool result = _device.StartCurveMotion((int)speed);
  350. if (result)
  351. {
  352. _platingDelayTime = DateTime.Now;
  353. if (!_recipe.HotPlatingCurrentOn) _hotPlatingRunTime = DateTime.Now;
  354. }
  355. return result;
  356. }
  357. /// <summary>
  358. /// Hot Plating
  359. /// </summary>
  360. /// <returns></returns>
  361. private bool HotPlating()
  362. {
  363. UpdateHotPlatingStepDatas();
  364. return StartPowerStep();
  365. }
  366. /// <summary>
  367. /// 启动PowerSupplier
  368. /// </summary>
  369. /// <returns></returns>
  370. private bool StartPowerStep()
  371. {
  372. if (_isZeroCurrent)
  373. {
  374. _startStepTime = DateTime.Now;
  375. return true;
  376. }
  377. if (string.IsNullOrEmpty(_side))
  378. {
  379. bool result = StartPowerStep(_device.SideAPowerSupplier);
  380. if (result)
  381. {
  382. result = StartPowerStep(_device.SideBPowerSupplier);
  383. if (!result)
  384. {
  385. _device.SideAPowerSupplier.DisableOperation("", null);
  386. _device.SideBPowerSupplier.DisableOperation("", null);
  387. return false;
  388. }
  389. else
  390. {
  391. _startStepTime = DateTime.Now;
  392. _hotPlatingRunTime = DateTime.Now;
  393. _stepWatch.Restart();
  394. _startRecipeStep = true;
  395. return true;
  396. }
  397. }
  398. else
  399. {
  400. _device.SideAPowerSupplier.DisableOperation("", null);
  401. return false;
  402. }
  403. }
  404. else
  405. {
  406. CellPowerSupplier cellPowerSupplier = GetSidePowerSupplier();
  407. bool result = StartPowerStep(cellPowerSupplier);
  408. if (!result)
  409. {
  410. cellPowerSupplier.DisableOperation("", null);
  411. _stepWatch.Restart();
  412. return false;
  413. }
  414. _startStepTime = DateTime.Now;
  415. _hotPlatingRunTime = DateTime.Now;
  416. _startRecipeStep = true;
  417. return true;
  418. }
  419. }
  420. /// <summary>
  421. /// 检验Powerstep是否启动完成
  422. /// </summary>
  423. /// <returns></returns>
  424. private bool CheckRecipeStepEndStatus()
  425. {
  426. if (_isZeroCurrent)
  427. {
  428. return true;
  429. }
  430. if (_startRecipeStep)
  431. {
  432. if (string.IsNullOrEmpty(_side))
  433. {
  434. bool resultA = _device.SideAPowerSupplier.Status == RState.End;
  435. bool resultB = _device.SideBPowerSupplier.Status == RState.End;
  436. return resultA && resultB;
  437. }
  438. else
  439. {
  440. CellPowerSupplier cellPowerSupplier = GetSidePowerSupplier();
  441. return cellPowerSupplier.Status == RState.End;
  442. }
  443. }
  444. return true;
  445. }
  446. /// <summary>
  447. /// 检验Powerstep是否启动完成
  448. /// </summary>
  449. /// <returns></returns>
  450. private bool CheckRecipeStepStopStatus()
  451. {
  452. if (_isZeroCurrent)
  453. {
  454. return false;
  455. }
  456. if (_startRecipeStep)
  457. {
  458. if (string.IsNullOrEmpty(_side))
  459. {
  460. bool resultA = _device.SideAPowerSupplier.Status == RState.Failed || _device.SideAPowerSupplier.Status == RState.Timeout;
  461. bool resultB = _device.SideBPowerSupplier.Status == RState.Failed || _device.SideBPowerSupplier.Status == RState.Timeout;
  462. return resultA && resultB;
  463. }
  464. else
  465. {
  466. CellPowerSupplier cellPowerSupplier = GetSidePowerSupplier();
  467. return cellPowerSupplier.Status == RState.Failed || cellPowerSupplier.Status == RState.Timeout;
  468. }
  469. }
  470. return false;
  471. }
  472. /// <summary>
  473. /// 获取单面PowerSupplier
  474. /// </summary>
  475. /// <returns></returns>
  476. private CellPowerSupplier GetSidePowerSupplier()
  477. {
  478. if (_side == SIDE_A)
  479. {
  480. return _device.SideAPowerSupplier;
  481. }
  482. else
  483. {
  484. return _device.SideBPowerSupplier;
  485. }
  486. }
  487. /// <summary>
  488. /// 启动
  489. /// </summary>
  490. /// <returns></returns>
  491. private bool StartPowerStep(CellPowerSupplier cellPowerSupplier)
  492. {
  493. bool result = cellPowerSupplier.StartSetStepPeriodNoWaitEnd(_powerSupplierStepPeriodDatas);
  494. if (!result)
  495. {
  496. cellPowerSupplier.DisableOperation("", null);
  497. return false;
  498. }
  499. return true;
  500. }
  501. /// <summary>
  502. /// 更新HotPlating step数据
  503. /// </summary>
  504. private void UpdateHotPlatingStepDatas()
  505. {
  506. ushort second = _powerSupplierStepPeriodDatas[0].Second;
  507. ushort minute = _powerSupplierStepPeriodDatas[0].Minute;
  508. ushort hour = _powerSupplierStepPeriodDatas[0].Hour;
  509. if (second + _recipe.PlatingDelaySeconds >= 60)
  510. {
  511. if (minute + 1 < 60)
  512. {
  513. _powerSupplierStepPeriodDatas[0].Minute = (ushort)(minute + 1);
  514. }
  515. else
  516. {
  517. _powerSupplierStepPeriodDatas[0].Minute = 0;
  518. _powerSupplierStepPeriodDatas[0].Hour = (ushort)(hour + 1);
  519. }
  520. _powerSupplierStepPeriodDatas[0].Second = (ushort)(second + _recipe.PlatingDelaySeconds - 60);
  521. }
  522. else
  523. {
  524. _powerSupplierStepPeriodDatas[0].Second = (ushort)(second + _recipe.PlatingDelaySeconds);
  525. }
  526. }
  527. /// <summary>
  528. /// 更新Power step步骤
  529. /// </summary>
  530. private void UpdatePowerStepDatas()
  531. {
  532. _isZeroCurrent = false;
  533. double current = 0;
  534. _totalMicrosecond = 0;
  535. _powerSupplierStepPeriodDatas.Clear();
  536. foreach (var item in _recipe.CurrentRampProfileSteps)
  537. {
  538. PowerSupplierStepPeriodData step = new PowerSupplierStepPeriodData();
  539. step.Current = item.ForwardAmps;
  540. step.Hour = (ushort)(item.CurrentRampDurartionSeconds / 3600);
  541. step.Minute = (ushort)((item.CurrentRampDurartionSeconds - step.Hour * 3600) / 60);
  542. step.Second = (ushort)(item.CurrentRampDurartionSeconds % 60);
  543. step.Microsecond = 0;
  544. step.Voltage = _recipe.VoltageWarningLevel;
  545. _powerSupplierStepPeriodDatas.Add(step);
  546. current += step.Current;
  547. _totalMicrosecond += item.CurrentRampDurartionSeconds * 1000;
  548. }
  549. if (current == 0)
  550. {
  551. _isZeroCurrent = true;
  552. }
  553. }
  554. /// <summary>
  555. /// 等待Plating Delay结束
  556. /// </summary>
  557. /// <returns></returns>
  558. private bool WaitPlatingDelay()
  559. {
  560. if (DateTime.Now.Subtract(_platingDelayTime).TotalSeconds >= _recipe.PlatingDelaySeconds)
  561. {
  562. return true;
  563. }
  564. return false;
  565. }
  566. /// <summary>
  567. /// 检验步骤是否完成
  568. /// </summary>
  569. /// <returns></returns>
  570. private bool CheckStepComplete()
  571. {
  572. if (_stepIndex >= _powerSupplierStepPeriodDatas.Count)
  573. {
  574. _stepWatch.Stop();
  575. LOG.WriteLog(eEvent.INFO_METAL, Module, $"step {_stepIndex} is over step count {_powerSupplierStepPeriodDatas.Count}");
  576. return true;
  577. }
  578. int firstDelay = 2000;
  579. if (_stepIndex == 0)
  580. {
  581. firstDelay = SC.GetValue<int>("Metal.CurrentCheckDelay") * 1000;
  582. }
  583. if (DateTime.Now.Subtract(_startStepTime).TotalMilliseconds >= firstDelay)
  584. {
  585. bool abnormal = CheckMetalDisable();
  586. if (abnormal)
  587. {
  588. return false;
  589. }
  590. }
  591. int length = _powerSupplierStepPeriodDatas[_stepIndex].Hour * 3600 + _powerSupplierStepPeriodDatas[_stepIndex].Minute * 60 +
  592. _powerSupplierStepPeriodDatas[_stepIndex].Second;
  593. if (DateTime.Now.Subtract(_startStepTime).TotalSeconds >= length)
  594. {
  595. _stepIndex++;
  596. if (_stepIndex >= _powerSupplierStepPeriodDatas.Count)
  597. {
  598. _stepWatch.Stop();
  599. LOG.WriteLog(eEvent.INFO_METAL, Module, $"step {_stepIndex} is over step count {_powerSupplierStepPeriodDatas.Count}");
  600. return true;
  601. }
  602. bool result = _device.ChangeCurveSpeedMotion((int)_recipe.CurrentRampProfileSteps[_stepIndex].ShearPlateSpeed);
  603. if (result)
  604. {
  605. LOG.WriteLog(eEvent.INFO_METAL, Module, $"step {_stepIndex} complete");
  606. _startStepTime = DateTime.Now;
  607. }
  608. return result;
  609. }
  610. double second = (double)_stepWatch.ElapsedMilliseconds / 1000;
  611. if (string.IsNullOrEmpty(_side))
  612. {
  613. _anodeAUsage += _device.SideAPowerSupplier.PowerSupplierData.Current * second / 3600;
  614. _anodeBUsage += _device.SideBPowerSupplier.PowerSupplierData.Current * second / 3600;
  615. }
  616. else if (_side == SIDE_A)
  617. {
  618. _anodeAUsage += _device.SideAPowerSupplier.PowerSupplierData.Current * second / 3600;
  619. }
  620. else
  621. {
  622. _anodeBUsage += _device.SideBPowerSupplier.PowerSupplierData.Current * second / 3600;
  623. }
  624. _stepWatch.Restart();
  625. return false;
  626. }
  627. /// <summary>
  628. /// 检验Power是否Disable
  629. /// </summary>
  630. /// <returns></returns>
  631. private bool CheckMetalDisable()
  632. {
  633. if (!_device.IsLinmotMotorOn)
  634. {
  635. LOG.WriteLog(eEvent.ERR_METAL, Module, "Linmot is not motor on");
  636. Abort();
  637. return true;
  638. }
  639. //CheckVotlageAndCurrent();
  640. if (!CheckVoltageAndCurrentValid())
  641. {
  642. Abort();
  643. return true;
  644. }
  645. if (!CheckMetalCellFlow()) //校验Manual 状态下run recipe的 cell flow和hold flow
  646. {
  647. Abort();
  648. return true;
  649. }
  650. return false;
  651. }
  652. /// <summary>
  653. /// 监控流量
  654. /// </summary>
  655. /// <returns></returns>
  656. private bool CheckMetalCellFlow()
  657. {
  658. MetalEntity metalEntity = Singleton<RouteManager>.Instance.GetModule<MetalEntity>(Module);
  659. if (metalEntity != null && metalEntity.IsManual)
  660. {
  661. MetalItem metalItem = MetalItemManager.Instance.GetMetalItem(Module);
  662. _metalType = metalItem.SubType;
  663. if (_metalType == STRATUS)
  664. {
  665. StandardHotMetalDevice _standardHotMetalDevice = DEVICE.GetDevice<StandardHotMetalDevice>(Module.ToString());
  666. //if (_standardHotMetalDevice.MetalDeviceData.CellFlow <= 0)
  667. //{
  668. // LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module} metal cell flow is 0");
  669. // return false;
  670. //}
  671. bool cellFlowSignalresulte = _cellFlowHoldoffTimeSignalMonitor.IsSignalAbnormal(_cellFlowHoldoffTime, _standardHotMetalDevice.MetalDeviceData.CellFlow, null, 0);
  672. if (cellFlowSignalresulte)
  673. {
  674. LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module} metal cell flow is 0");
  675. return false;
  676. }
  677. }
  678. }
  679. return true;
  680. }
  681. /// <summary>
  682. /// 检验电压电流的合理性
  683. /// </summary>
  684. /// <returns></returns>
  685. private bool CheckVoltageAndCurrentValid()
  686. {
  687. //零电流不检验
  688. if (_isZeroCurrent)
  689. {
  690. return true;
  691. }
  692. if (string.IsNullOrEmpty(_side))
  693. {
  694. if (!_device.SideAPowerSupplier.PowerSupplierData.Enabled)
  695. {
  696. LOG.WriteLog(eEvent.ERR_METAL, Module, "PowerA disable");
  697. return false;
  698. }
  699. if (!_device.SideBPowerSupplier.PowerSupplierData.Enabled)
  700. {
  701. LOG.WriteLog(eEvent.ERR_METAL, Module, "PowerB disable");
  702. return false;
  703. }
  704. }
  705. else
  706. {
  707. if (_side == SIDE_A && !_device.SideAPowerSupplier.PowerSupplierData.Enabled)
  708. {
  709. LOG.WriteLog(eEvent.ERR_METAL, Module, "PowerA disable");
  710. return false;
  711. }
  712. if (_side == SIDE_B && !_device.SideBPowerSupplier.PowerSupplierData.Enabled)
  713. {
  714. LOG.WriteLog(eEvent.ERR_METAL, Module, "PowerB disable");
  715. return false;
  716. }
  717. }
  718. double sideACurrent = _device.SideAPowerSupplier.PowerSupplierData.Current;
  719. double sideAVoltage = _device.SideAPowerSupplier.PowerSupplierData.Voltage;
  720. double sideBCurrent = _device.SideBPowerSupplier.PowerSupplierData.Current;
  721. double sideBVoltage = _device.SideBPowerSupplier.PowerSupplierData.Voltage;
  722. if (string.IsNullOrEmpty(_side))
  723. {
  724. bool sideAValid = CheckSidePowerInvalid(sideACurrent, sideAVoltage, SIDE_A);
  725. if (sideAValid)
  726. {
  727. return false;
  728. }
  729. bool sideBValid = CheckSidePowerInvalid(sideBCurrent, sideBVoltage, SIDE_B);
  730. if (sideBValid)
  731. {
  732. return false;
  733. }
  734. }
  735. else
  736. {
  737. if (_side == SIDE_A)
  738. {
  739. bool sideAValid = CheckSidePowerInvalid(sideACurrent, sideAVoltage, SIDE_A);
  740. if (sideAValid)
  741. {
  742. return false;
  743. }
  744. }
  745. else
  746. {
  747. bool sideBValid = CheckSidePowerInvalid(sideBCurrent, sideBVoltage, SIDE_B);
  748. if (sideBValid)
  749. {
  750. return false;
  751. }
  752. }
  753. }
  754. return true;
  755. }
  756. /// <summary>
  757. /// 检验电流和电压合理性
  758. /// </summary>
  759. /// <param name="current"></param>
  760. /// <param name="voltage"></param>
  761. /// <param name="side"></param>
  762. /// <returns></returns>
  763. private bool CheckSidePowerInvalid(double current, double voltage, string side)
  764. {
  765. double maxVoltage = _recipe.VolatageLimitMax;
  766. double warnVoltage = _recipe.VoltageWarningLevel;
  767. double minVoltage = _recipe.VolatageLimitMin;
  768. //if (voltage > maxVoltage)
  769. //{
  770. // LOG.WriteLog(eEvent.ERR_METAL, Module, $"{side} voltage {voltage} is large than recipe max voltage {maxVoltage}");
  771. // return true;
  772. //}
  773. //if (voltage < minVoltage)
  774. //{
  775. // LOG.WriteLog(eEvent.ERR_METAL, Module, $"{side} voltage {voltage} is less than recipe min voltage {minVoltage}");
  776. // return true;
  777. //}
  778. bool voltageSignalresulte = _voltageHoldoffTimeSignalMonitor.IsSignalAbnormal(_voltageHoldoffTime, voltage, maxVoltage, minVoltage);
  779. if (voltageSignalresulte)
  780. {
  781. if (voltage > maxVoltage)
  782. {
  783. LOG.WriteLog(eEvent.ERR_METAL, Module, $"{side} voltage {voltage} is large than recipe max voltage {maxVoltage}");
  784. }
  785. if (voltage < minVoltage)
  786. {
  787. LOG.WriteLog(eEvent.ERR_METAL, Module, $"{side} voltage {voltage} is less than recipe min voltage {minVoltage}");
  788. }
  789. return true;
  790. }
  791. if (voltage > warnVoltage)
  792. {
  793. string str = $"{side} voltage is {voltage} in warning";
  794. if (AlarmListManager.Instance.AddWarn(Module, $"{side} voltage", str))
  795. {
  796. LOG.WriteLog(eEvent.WARN_PREWET, Module, str);
  797. }
  798. }
  799. double maxErrorCurrent = _powerSupplierStepPeriodDatas[_stepIndex].Current * (double)(1 + _recipe.FaultPercent * 0.01);
  800. double minErrorCurrent = _powerSupplierStepPeriodDatas[_stepIndex].Current * (double)(1 - _recipe.FaultPercent * 0.01);
  801. double maxWarnCurrent = _powerSupplierStepPeriodDatas[_stepIndex].Current * (double)(1 + _recipe.CurrentWarningLevel * 0.01);
  802. double minWarnCurrent = _powerSupplierStepPeriodDatas[_stepIndex].Current * (double)(1 - _recipe.CurrentWarningLevel * 0.01);
  803. //if (current > maxErrorCurrent)
  804. //{
  805. // LOG.WriteLog(eEvent.ERR_METAL, Module, $"{side} current {current} is large than recipe max current {maxErrorCurrent}");
  806. // return true;
  807. //}
  808. //if (current < minErrorCurrent)
  809. //{
  810. // LOG.WriteLog(eEvent.ERR_METAL, Module, $"{side} current {current} is less than recipe min current {minErrorCurrent}");
  811. // return true;
  812. //}
  813. bool currentSignalresulte = _currentHoldoffTimeSignalMonitor.IsSignalAbnormal(_currentHoldoffTime, current, maxErrorCurrent, minErrorCurrent);
  814. if (currentSignalresulte)
  815. {
  816. if (current > maxErrorCurrent)
  817. {
  818. LOG.WriteLog(eEvent.ERR_METAL, Module, $"{side} current {current} is large than recipe max current {maxErrorCurrent}");
  819. }
  820. if (current < minErrorCurrent)
  821. {
  822. LOG.WriteLog(eEvent.ERR_METAL, Module, $"{side} current {current} is less than recipe min current {minErrorCurrent}");
  823. }
  824. return true;
  825. }
  826. if ((current <= maxErrorCurrent && current >= maxWarnCurrent) || (current >= minErrorCurrent && current <= minWarnCurrent))
  827. {
  828. string str = $"{side} current {current} is in warning";
  829. if (AlarmListManager.Instance.AddWarn(Module, $"{side} current", str))
  830. {
  831. LOG.WriteLog(eEvent.WARN_PREWET, Module, str);
  832. }
  833. }
  834. return false;
  835. }
  836. /// <summary>
  837. /// 停止Linmot
  838. /// </summary>
  839. /// <returns></returns>
  840. private bool StopLinmot()
  841. {
  842. return _device.StopLinmot();
  843. }
  844. /// <summary>
  845. /// 停止电源
  846. /// </summary>
  847. /// <returns></returns>
  848. private bool StopPowerSupplier()
  849. {
  850. if (string.IsNullOrEmpty(_side))
  851. {
  852. _device.SideAPowerSupplier.DisableOperation("", null);
  853. _device.SideBPowerSupplier.DisableOperation("", null);
  854. }
  855. else
  856. {
  857. CellPowerSupplier cellPowerSupplier = GetSidePowerSupplier();
  858. cellPowerSupplier.DisableOperation("", null);
  859. }
  860. return true;
  861. }
  862. /// <summary>
  863. /// 切换成正常模式
  864. /// </summary>
  865. /// <returns></returns>
  866. private bool SwitchToNormal()
  867. {
  868. if (string.IsNullOrEmpty(_side))
  869. {
  870. _device.SideAPowerSupplier.SwitchPowerRunModel((int)PowerRunModelEnum.Normal);
  871. _device.SideBPowerSupplier.SwitchPowerRunModel((int)PowerRunModelEnum.Normal);
  872. }
  873. else
  874. {
  875. CellPowerSupplier cellPowerSupplier = GetSidePowerSupplier();
  876. cellPowerSupplier.SwitchPowerRunModel((int)PowerRunModelEnum.Normal);
  877. }
  878. return true;
  879. }
  880. /// <summary>
  881. /// 启动
  882. /// </summary>
  883. /// <param name="objs"></param>
  884. /// <returns></returns>
  885. public RState Start(params object[] objs)
  886. {
  887. _isVotlageWarningA = false;
  888. _isVotlageWarningB = false;
  889. _isCurrentWarningA = false;
  890. _isCurrentWarningB = false;
  891. _recipe = objs[0] as DepRecipe;
  892. if (_recipe == null)
  893. {
  894. LOG.WriteLog(eEvent.ERR_METAL, Module, "recipe is null");
  895. return RState.Failed;
  896. }
  897. if (objs.Length > 1)
  898. {
  899. _side = objs[1].ToString();
  900. }
  901. _startRecipeStep = false;
  902. _anodeAUsage = 0;
  903. _anodeBUsage = 0;
  904. _device = DEVICE.GetDevice<MetalCellDevice>(Module);
  905. _metalEntity = Singleton<RouteManager>.Instance.GetModule<MetalEntity>(Module);
  906. UpdatePowerStepDatas();
  907. _stepIndex = 0;
  908. _header.SoftWareVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
  909. _header.Recipe = $"{_recipe.Ppid}.dep.rcp";
  910. if (SC.ContainsItem("System.ToolID")) _header.ToolID = SC.GetStringValue("System.ToolID");
  911. //lotTract记录SequenceRecipe
  912. MetalEntity metalEntity = Singleton<RouteManager>.Instance.GetModule<MetalEntity>(Module);
  913. if (metalEntity.WaferHolderInfo != null && metalEntity.WaferHolderInfo.SequenceRecipe != null && !String.IsNullOrEmpty(metalEntity.WaferHolderInfo.SequenceRecipe.Ppid.ToString()))
  914. {
  915. _header.SequenceRecipe = metalEntity.WaferHolderInfo.SequenceRecipe.Ppid.ToString();
  916. _header.ProcessTransferList = new List<string>();
  917. _header.ProcessTransferList.AddRange(metalEntity.WaferHolderInfo.SchedulerModules);
  918. metalEntity.WaferHolderInfo.SchedulerModules.Clear();
  919. }
  920. _facilities = DEVICE.GetDevice<SystemFacilities>("System.Facilities");
  921. if (_facilities == null)
  922. {
  923. LOG.WriteLog(eEvent.ERR_METAL, Module, "Facility is null");
  924. return RState.Failed;
  925. }
  926. string reservoirName = ReservoirItemManager.Instance.GetReservoirByMetal(Module);
  927. MetalItem metalItem = MetalItemManager.Instance.GetMetalItem(Module);
  928. _metalType = metalItem.SubType;
  929. if (_metalType == STRATUS)
  930. {
  931. _standardHotReservoirDevice = DEVICE.GetDevice<StandardHotReservoirDevice>(reservoirName);
  932. _standardHotMetalDevice = DEVICE.GetDevice<StandardHotMetalDevice>(Module);
  933. if (_standardHotReservoirDevice == null || _standardHotMetalDevice == null)
  934. {
  935. LOG.WriteLog(eEvent.ERR_METAL, Module, $"metal or reservoir device is null");
  936. return RState.Failed;
  937. }
  938. if (_standardHotMetalDevice.MetalDeviceData.CellFlow <= 0) //检查cell flow
  939. {
  940. LOG.WriteLog(eEvent.ERR_METAL, Module, $"reservoir metal cell flow is 0");
  941. return RState.Failed;
  942. }
  943. }
  944. else
  945. {
  946. _compactMembranReservoirDevice = DEVICE.GetDevice<CompactMembranReservoirDevice>(reservoirName);
  947. _compactMembranMetalDevice = DEVICE.GetDevice<CompactMembranMetalDevice>(Module);
  948. if (_compactMembranMetalDevice == null || _compactMembranReservoirDevice == null)
  949. {
  950. LOG.WriteLog(eEvent.ERR_METAL, Module, $"metal or reservoir device is null");
  951. return RState.Failed;
  952. }
  953. if (_compactMembranMetalDevice.MetalDeviceData.CellFlow <= 0) //检查cell flow
  954. {
  955. LOG.WriteLog(eEvent.ERR_METAL, Module, $"reservoir metal cell flow is 0");
  956. return RState.Failed;
  957. }
  958. if (_compactMembranMetalDevice.ANACellFlow.CounterValue <= 0) //检查hold flow
  959. {
  960. LOG.WriteLog(eEvent.ERR_METAL, Module, $"reservoir metal AnodeA flow is 0");
  961. return RState.Failed;
  962. }
  963. if (_compactMembranMetalDevice.ANBCellFlow.CounterValue <= 0)
  964. {
  965. LOG.WriteLog(eEvent.ERR_METAL, Module, $"reservoir metal AnodeB flow is 0");
  966. return RState.Failed;
  967. }
  968. }
  969. ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(reservoirName);
  970. _temperatureController = DEVICE.GetDevice<TemperatureController>(reservoirItem.TCID);
  971. if (_temperatureController == null)
  972. {
  973. LOG.WriteLog(eEvent.ERR_METAL, Module, $"Temperature controller is null");
  974. return RState.Failed;
  975. }
  976. _persistentValue = MetalPersistentManager.Instance.GetMetalPersistentValue(Module);
  977. if (_persistentValue == null)
  978. {
  979. LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} Persistent Value Object is not exist");
  980. return RState.Failed;
  981. }
  982. _lotTackTime = DateTime.Now;
  983. return Runner.Start(Module, "Metal run recipe");
  984. }
  985. public void clearLotTrack()
  986. {
  987. _datas.Clear();
  988. }
  989. public void resetMetalUsage()
  990. {
  991. _anodeAUsage = 0;
  992. _anodeBUsage = 0;
  993. }
  994. }
  995. }