using Aitex.Core.RT.Device; using Aitex.Core.RT.Log; using Aitex.Core.RT.Routine; using Aitex.Core.RT.SCCore; using Aitex.Core.Util; using CyberX8_Core; using CyberX8_RT.Devices.Facilities; using CyberX8_RT.Devices.Metal; using CyberX8_RT.Devices.PowerSupplier; using CyberX8_RT.Devices.Reservoir; using CyberX8_RT.Devices.Temperature; using MECF.Framework.Common.Alarm; using MECF.Framework.Common.CommonData; using MECF.Framework.Common.CommonData.Metal; using MECF.Framework.Common.CommonData.PowerSupplier; using MECF.Framework.Common.Persistent.Reservoirs; using MECF.Framework.Common.RecipeCenter; using MECF.Framework.Common.Routine; using MECF.Framework.Common.ToolLayout; using System; using System.Collections.Generic; using System.Diagnostics; namespace CyberX8_RT.Modules.Metal { public class ReservoirRunRecipeRoutine : RoutineBase,IRoutine { #region 常量 private const int LOTTRACK_TIME = 1000; private const string CDA_1_PRESSURE_VALUE = "CDA1Pressure"; private const string CDA_2_PRESSURE_VALUE = "CDA2Pressure"; private const string STRATUS = "Stratus"; #endregion private enum RecipeStep { PlatingDelay, PlatingDelayCheck, HotPlating, WaitPlatingDelay, RunPowerStep, RunPowerStepWait, LoopStart, LoopCheckRun, LoopEnd, StopLinmot, StopPowerSuppliers, SwitchToNormal, WaferHolderUnclampOn, End } #region 常量 private const string SIDE_A = "SideA"; private const string SIDE_B = "SideB"; #endregion #region 内部变量 /// /// recipe /// private DepRecipe _recipe; /// /// Plate Delay时间 /// private DateTime _platingDelayTime = DateTime.Now; /// /// 启动步骤时间 /// private DateTime _startStepTime = DateTime.Now; /// /// 电量步骤计数 /// private Stopwatch _stepWatch = new Stopwatch(); /// /// Power 集合 /// List _powerSupplierStepPeriodDatas = new List(); /// /// 单面 /// private string _side; /// /// 步骤索引 /// private int _stepIndex = 0; /// /// 设备 /// private MetalCellDevice _device; /// /// 设备Entity /// private MetalEntity _metalEntity; /// /// A面电量 /// private double _anodeAUsage; /// /// B面电量 /// private double _anodeBUsage; /// /// 是否启动recipe步骤 /// private bool _startRecipeStep = false; /// /// lock track time /// private DateTime _lotTackTime = DateTime.Now; /// /// LotTrack数据 /// private List _datas = new List(); /// /// LotTrack文件头数据 /// private LotTrackFileHeaderCommonData _header = new LotTrackFileHeaderCommonData(); /// /// Facilities /// private SystemFacilities _facilities; /// /// StandardHot Reservoir Device /// private StandardHotReservoirDevice _standardHotReservoirDevice; /// /// StandardHot Metal Device /// private StandardHotMetalDevice _standardHotMetalDevice; /// /// CompactMembran Reservoir Device /// private CompactMembranReservoirDevice _compactMembranReservoirDevice; /// /// CompactMembran Reservoir Metal /// private CompactMembranMetalDevice _compactMembranMetalDevice; /// /// MetalType /// private string _metalType; /// /// TC device /// private TemperatureController _temperatureController; /// /// Persistent value /// private MetalPersistentValue _persistentValue; /// /// PlatingDelay计时 /// private DateTime _hotPlatingRunTime; /// /// 是否处于warning状态 /// private bool _isVotlageWarningA = false; private bool _isVotlageWarningB = false; private bool _isCurrentWarningA = false; private bool _isCurrentWarningB = false; #endregion #region 属性 /// /// A面电量 /// public double AnodeAUsage { get { return _anodeAUsage; } } /// /// B面电量 /// public double AnodeBUsage { get { return _anodeBUsage; } } /// /// LotTrack数据 /// public List MetalLotTrackDatas { get { return _datas; } } /// /// LotTrack文件头数据 /// public LotTrackFileHeaderCommonData MetalLotTrackHeaderDatas { get { return _header; } } #endregion /// /// 构造函数 /// public ReservoirRunRecipeRoutine(string moduleName):base(moduleName) { } /// /// 中止 /// public void Abort() { if (_device != null) { _device.SideAPowerSupplier.DisableOperation("", null); _device.SideBPowerSupplier.DisableOperation("", null); _device.SideAPowerSupplier.SwitchPowerRunModel((int)PowerRunModelEnum.Normal); _device.SideBPowerSupplier.SwitchPowerRunModel((int)PowerRunModelEnum.Normal); if (_device.IsLinmotMotorOn) { _device.StopLinmot(); } } Runner.Stop("Manual Abort"); } /// /// 监控 /// /// public RState Monitor() { LottrackRecord(); Runner.Run(RecipeStep.PlatingDelay, PlatingDelay, _delay_1ms) .WaitWithStopCondition(RecipeStep.PlatingDelayCheck,()=>{ return _device.CheckLinmotRoutineEnd(); }, () => { return _device.CheckLinmotRoutineError(); } ) .RunIf(RecipeStep.HotPlating, _recipe.HotPlatingCurrentOn, HotPlating, _delay_1ms) .RunIf(RecipeStep.WaitPlatingDelay, !_recipe.HotPlatingCurrentOn, NullFun, WaitPlatingDelay, _recipe.PlatingDelaySeconds * 1000 + 10000) .RunIf(RecipeStep.RunPowerStep, !_recipe.HotPlatingCurrentOn, StartPowerStep, _delay_1ms) .WaitWithStopCondition(RecipeStep.RunPowerStepWait,CheckRecipeStepEndStatus,CheckRecipeStepStopStatus,_delay_5s) .LoopStart(RecipeStep.LoopStart, "Loop update linmot speed", _powerSupplierStepPeriodDatas.Count, NullFun, _delay_1ms) .LoopRunWithStopStatus(RecipeStep.LoopCheckRun, CheckStepComplete, () => { return false; }) .LoopEnd(RecipeStep.LoopEnd, NullFun, _delay_1ms) .Run(RecipeStep.StopLinmot, StopLinmot, _delay_1ms) .Run(RecipeStep.StopPowerSuppliers, StopPowerSupplier, _delay_1ms) .Run(RecipeStep.SwitchToNormal, SwitchToNormal, _delay_1ms) .End(RecipeStep.End, NullFun, _delay_1ms); return Runner.Status; } /// /// 记录Lottrack /// private void LottrackRecord() { //记录Lottrack if (DateTime.Now.Subtract(_lotTackTime).TotalMilliseconds >= LOTTRACK_TIME) { AddLotTrackData(); _lotTackTime = DateTime.Now; } } /// /// 获取Lot Track数据 /// /// public void AddLotTrackData() { MetalLotTrackData data = new MetalLotTrackData(); if(_metalType == STRATUS) { data.Flow = _standardHotMetalDevice.MetalDeviceData.CellFlow; data.ANLevel = 0; data.CALevel = _standardHotReservoirDevice.ReservoirData.Level; data.CAPumpSpeed = 0; } else { data.Flow = _compactMembranMetalDevice.MetalDeviceData.CellFlow; data.ANLevel = _compactMembranReservoirDevice.ReservoirData.ANLevel; data.CALevel = _compactMembranReservoirDevice.ReservoirData.CALevel; data.CAPumpSpeed = _compactMembranReservoirDevice.ReservoirData.CAPumpSpeed; } data.RunTime = 0; data.ClampCycleEngaged = _recipe.CycleClampsEnable; data.Temperature = _temperatureController.TemperatureData.ReserviorTemperature; data.TimeStamp = DateTime.Now; data.PowerSupplyA = _device.SideAPowerSupplier.Name; data.PowerSupplyB = _device.SideBPowerSupplier.Name; data.PosVoltageA = _device.SideAPowerSupplier.PowerSupplierData.Voltage; data.PosCurrentA = _device.SideAPowerSupplier.PowerSupplierData.Current; data.PosVoltageB = _device.SideBPowerSupplier.PowerSupplierData.Voltage; data.PosCurrentB = _device.SideBPowerSupplier.PowerSupplierData.Current; data.CDA_1_Pressure = _facilities.GetCommonLimitDataByName(CDA_1_PRESSURE_VALUE).Value; data.CDA_2_Pressure = _facilities.GetCommonLimitDataByName(CDA_2_PRESSURE_VALUE).Value; int maxStep = _powerSupplierStepPeriodDatas.Count; if (_stepIndex < maxStep) { data.StepNum = _stepIndex + 1; int length = _powerSupplierStepPeriodDatas[_stepIndex].Hour * 3600 + _powerSupplierStepPeriodDatas[_stepIndex].Minute * 60 + _powerSupplierStepPeriodDatas[_stepIndex].Second; data.DurationRef = length; data.ShearPlateSpeed = _recipe.CurrentRampProfileSteps[_stepIndex].ShearPlateSpeed; data.CurrentSP = _recipe.CurrentRampProfileSteps[_stepIndex].ForwardAmps; } else { data.StepNum = maxStep; int length = _powerSupplierStepPeriodDatas[maxStep - 1].Hour * 3600 + _powerSupplierStepPeriodDatas[maxStep - 1].Minute * 60 + _powerSupplierStepPeriodDatas[maxStep - 1].Second; data.DurationRef = length; data.ShearPlateSpeed = _recipe.CurrentRampProfileSteps[maxStep - 1].ShearPlateSpeed; data.CurrentSP = _recipe.CurrentRampProfileSteps[maxStep - 1].ForwardAmps; } RecipeStep step = (RecipeStep)Runner.CurrentStep; if (step <= RecipeStep.WaitPlatingDelay) { if (!_recipe.HotPlatingCurrentOn) { data.RunTime = DateTime.Now.Subtract(_hotPlatingRunTime).TotalSeconds; data.DurationRef = _recipe.PlatingDelaySeconds; data.StepNum = 0; } else { data.RunTime = 0; data.DurationRef = _recipe.PlatingDelaySeconds; data.StepNum = 0; } } else { if (_recipe.HotPlatingCurrentOn) { if(DateTime.Now.Subtract(_hotPlatingRunTime).TotalSeconds <= _recipe.PlatingDelaySeconds) { data.RunTime = DateTime.Now.Subtract(_hotPlatingRunTime).TotalSeconds; data.DurationRef = _recipe.PlatingDelaySeconds; data.StepNum = 0; } else { if(data.StepNum == 1) data.DurationRef -= _recipe.PlatingDelaySeconds; data.RunTime = (data.StepNum == 1)?(DateTime.Now.Subtract(_startStepTime).TotalSeconds - _recipe.PlatingDelaySeconds): DateTime.Now.Subtract(_startStepTime).TotalSeconds; } } else { data.RunTime = DateTime.Now.Subtract(_startStepTime).TotalSeconds; } } _datas.Add(data); } /// /// Plate Delay /// /// private bool PlatingDelay() { double speed = _recipe.CurrentRampProfileSteps[0].ShearPlateSpeed; bool result = _device.StartCurveMotion((int)speed); if (result) { _platingDelayTime = DateTime.Now; if(!_recipe.HotPlatingCurrentOn) _hotPlatingRunTime = DateTime.Now; } return result; } /// /// Hot Plating /// /// private bool HotPlating() { UpdateHotPlatingStepDatas(); return StartPowerStep(); } /// /// 启动PowerSupplier /// /// private bool StartPowerStep() { if (string.IsNullOrEmpty(_side)) { bool result = StartPowerStep(_device.SideAPowerSupplier); if (result) { result = StartPowerStep(_device.SideBPowerSupplier); if (!result) { _device.SideAPowerSupplier.DisableOperation("", null); _device.SideBPowerSupplier.DisableOperation("", null); return false; } else { _startStepTime = DateTime.Now; _hotPlatingRunTime = DateTime.Now; _stepWatch.Restart(); _startRecipeStep = true; return true; } } else { _device.SideAPowerSupplier.DisableOperation("", null); return false; } } else { CellPowerSupplier cellPowerSupplier = GetSidePowerSupplier(); bool result = StartPowerStep(cellPowerSupplier); if (!result) { cellPowerSupplier.DisableOperation("", null); _stepWatch.Restart(); return false; } _startStepTime = DateTime.Now; _hotPlatingRunTime = DateTime.Now; _startRecipeStep = true; return true; } } /// /// 检验Powerstep是否启动完成 /// /// private bool CheckRecipeStepEndStatus() { if (_startRecipeStep) { if (string.IsNullOrEmpty(_side)) { bool resultA = _device.SideAPowerSupplier.Status == RState.End; bool resultB=_device.SideBPowerSupplier.Status == RState.End; return resultA && resultB; } else { CellPowerSupplier cellPowerSupplier = GetSidePowerSupplier(); return cellPowerSupplier.Status == RState.End; } } return true; } /// /// 检验Powerstep是否启动完成 /// /// private bool CheckRecipeStepStopStatus() { if (_startRecipeStep) { if (string.IsNullOrEmpty(_side)) { bool resultA = _device.SideAPowerSupplier.Status == RState.Failed||_device.SideAPowerSupplier.Status==RState.Timeout; bool resultB = _device.SideBPowerSupplier.Status == RState.Failed||_device.SideBPowerSupplier.Status==RState.Timeout; return resultA && resultB; } else { CellPowerSupplier cellPowerSupplier = GetSidePowerSupplier(); return cellPowerSupplier.Status == RState.Failed||cellPowerSupplier.Status==RState.Timeout; } } return false; } /// /// 获取单面PowerSupplier /// /// private CellPowerSupplier GetSidePowerSupplier() { if (_side == SIDE_A) { return _device.SideAPowerSupplier; } else { return _device.SideBPowerSupplier; } } /// /// 启动 /// /// private bool StartPowerStep(CellPowerSupplier cellPowerSupplier) { bool result = cellPowerSupplier.StartSetStepPeriodNoWaitEnd(_powerSupplierStepPeriodDatas); if (!result) { cellPowerSupplier.DisableOperation("", null); return false; } return true; } /// /// 更新HotPlating step数据 /// private void UpdateHotPlatingStepDatas() { ushort second = _powerSupplierStepPeriodDatas[0].Second; ushort minute = _powerSupplierStepPeriodDatas[0].Minute; ushort hour = _powerSupplierStepPeriodDatas[0].Hour; if (second + _recipe.PlatingDelaySeconds >= 60) { if (minute + 1 < 60) { _powerSupplierStepPeriodDatas[0].Minute = (ushort)(minute + 1); } else { _powerSupplierStepPeriodDatas[0].Minute = 0; _powerSupplierStepPeriodDatas[0].Hour = (ushort)(hour + 1); } _powerSupplierStepPeriodDatas[0].Second = (ushort)(second + _recipe.PlatingDelaySeconds - 60); } else { _powerSupplierStepPeriodDatas[0].Second = (ushort)(second + _recipe.PlatingDelaySeconds); } } /// /// 更新Power step步骤 /// private void UpdatePowerStepDatas() { _powerSupplierStepPeriodDatas.Clear(); foreach (var item in _recipe.CurrentRampProfileSteps) { PowerSupplierStepPeriodData step = new PowerSupplierStepPeriodData(); step.Current = item.ForwardAmps; step.Hour = (ushort)(item.CurrentRampDurartionSeconds / 3600); step.Minute = (ushort)(item.CurrentRampDurartionSeconds / 60); step.Second = (ushort)(item.CurrentRampDurartionSeconds % 60); step.Microsecond = 0; _powerSupplierStepPeriodDatas.Add(step); } } /// /// 等待Plating Delay结束 /// /// private bool WaitPlatingDelay() { if (DateTime.Now.Subtract(_platingDelayTime).TotalSeconds >= _recipe.PlatingDelaySeconds) { return true; } return false; } /// /// 检验步骤是否完成 /// /// private bool CheckStepComplete() { if (_stepIndex >= _powerSupplierStepPeriodDatas.Count) { _stepWatch.Stop(); LOG.WriteLog(eEvent.INFO_METAL, Module, $"step {_stepIndex} is over step count {_powerSupplierStepPeriodDatas.Count}"); return true; } if (DateTime.Now.Subtract(_startStepTime).TotalMilliseconds >= 2000) { bool abnormal = CheckMetalDisable(); if (abnormal) { return false; } } int length = _powerSupplierStepPeriodDatas[_stepIndex].Hour * 3600 + _powerSupplierStepPeriodDatas[_stepIndex].Minute * 60 + _powerSupplierStepPeriodDatas[_stepIndex].Second; if (DateTime.Now.Subtract(_startStepTime).TotalSeconds >= length) { _stepIndex++; if (_stepIndex >= _powerSupplierStepPeriodDatas.Count) { _stepWatch.Stop(); LOG.WriteLog(eEvent.INFO_METAL, Module, $"step {_stepIndex} is over step count {_powerSupplierStepPeriodDatas.Count}"); return true; } bool result = _device.ChangeCurveSpeedMotion((int)_recipe.CurrentRampProfileSteps[_stepIndex].ShearPlateSpeed); if (result) { LOG.WriteLog(eEvent.INFO_METAL, Module, $"step {_stepIndex} complete"); _startStepTime = DateTime.Now; } return result; } double second = (double)_stepWatch.ElapsedMilliseconds / 1000; if (string.IsNullOrEmpty(_side)) { _anodeAUsage += _device.SideAPowerSupplier.PowerSupplierData.Current * second / 3600; _anodeBUsage += _device.SideBPowerSupplier.PowerSupplierData.Current * second / 3600; } else if(_side==SIDE_A) { _anodeAUsage += _device.SideAPowerSupplier.PowerSupplierData.Current * second / 3600; } else { _anodeBUsage += _device.SideBPowerSupplier.PowerSupplierData.Current * second / 3600; } _stepWatch.Restart(); return false; } /// /// 检验Power是否Disable /// /// private bool CheckMetalDisable() { if (string.IsNullOrEmpty(_side)) { if (!_device.SideAPowerSupplier.PowerSupplierData.Enabled) { LOG.WriteLog(eEvent.ERR_METAL, Module, "PowerA disable"); Abort(); return true; } if (!_device.SideBPowerSupplier.PowerSupplierData.Enabled) { LOG.WriteLog(eEvent.ERR_METAL, Module, "PowerB disable"); Abort(); return true; } } else { if (_side == SIDE_A && !_device.SideAPowerSupplier.PowerSupplierData.Enabled) { LOG.WriteLog(eEvent.ERR_METAL, Module, "PowerA disable"); Abort(); return true; } if (_side == SIDE_B && !_device.SideBPowerSupplier.PowerSupplierData.Enabled) { LOG.WriteLog(eEvent.ERR_METAL, Module, "PowerB disable"); Abort(); return true; } } if (!_device.IsLinmotMotorOn) { LOG.WriteLog(eEvent.ERR_METAL, Module, "Linmot is not motor on"); Abort(); return true; } //CheckVotlageAndCurrent(); if (!CheckVoltageAndCurrentValid()) { Abort(); return true; } return false; } /// /// 检验电压电流的合理性 /// /// private bool CheckVoltageAndCurrentValid() { double sideACurrent = _device.SideAPowerSupplier.PowerSupplierData.Current; double sideAVoltage = _device.SideAPowerSupplier.PowerSupplierData.Voltage; double sideBCurrent = _device.SideBPowerSupplier.PowerSupplierData.Current; double sideBVoltage = _device.SideBPowerSupplier.PowerSupplierData.Voltage; if (string.IsNullOrEmpty(_side)) { bool sideAValid = CheckSidePowerInvalid(sideACurrent, sideAVoltage, SIDE_A); if (sideAValid) { return false; } bool sideBValid = CheckSidePowerInvalid(sideBCurrent, sideBVoltage, SIDE_B); if (sideBValid) { return false; } } else { if (_side == SIDE_A) { bool sideAValid = CheckSidePowerInvalid(sideACurrent, sideAVoltage, SIDE_A); if (sideAValid) { return false; } } else { bool sideBValid = CheckSidePowerInvalid(sideBCurrent, sideBVoltage, SIDE_B); if (sideBValid) { return false; } } } return true; } /// /// 监控电流和电压 /// private void CheckVotlageAndCurrent() { if (string.IsNullOrEmpty(_side)) { //监控电压A面 MetalEntity metalEntity = Singleton.Instance.GetModule(Module); if (_device.SideAPowerSupplier.PowerSupplierData.Voltage > _recipe.VoltageWarningLevel && _device.SideAPowerSupplier.PowerSupplierData.Voltage < _recipe.VolatageLimitMax && !_isVotlageWarningA) { _isVotlageWarningA = true; LOG.WriteLog(eEvent.WARN_METAL, Module, $"Current SideA Votlage {_device.SideAPowerSupplier.PowerSupplierData.Voltage} is large than recipe VolatageWarningLevel {_recipe.VoltageWarningLevel}"); } if (_device.SideAPowerSupplier.PowerSupplierData.Voltage < _recipe.VolatageLimitMin) { if (metalEntity != null) { metalEntity.PostMsg(MetalMsg.Error); } Abort(); LOG.WriteLog(eEvent.ERR_METAL, Module, $"Current SideA Votlage {_device.SideAPowerSupplier.PowerSupplierData.Voltage} is lower than recipe VolatageLimitMin {_recipe.VolatageLimitMin}"); } if (_device.SideAPowerSupplier.PowerSupplierData.Voltage > _recipe.VolatageLimitMax) { if (metalEntity != null) { metalEntity.PostMsg(MetalMsg.Error); } Abort(); LOG.WriteLog(eEvent.ERR_METAL, Module, $"Current SideA Votlage {_device.SideAPowerSupplier.PowerSupplierData.Voltage} is large than recipe VolatageLimitMin {_recipe.VolatageLimitMax}"); } //监控电流A面 double currentWarningFault = _powerSupplierStepPeriodDatas[_stepIndex].Current * _recipe.CurrentWarningLevel * 0.01; double currentErrorFault = _powerSupplierStepPeriodDatas[_stepIndex].Current * _recipe.FaultPercent * 0.01; if (_device.SideAPowerSupplier.PowerSupplierData.Current > _powerSupplierStepPeriodDatas[_stepIndex].Current + currentErrorFault || _device.SideAPowerSupplier.PowerSupplierData.Current < _powerSupplierStepPeriodDatas[_stepIndex].Current - currentErrorFault) { if (metalEntity != null) { metalEntity.PostMsg(MetalMsg.Error); } Abort(); LOG.WriteLog(eEvent.ERR_METAL, Module, $"Current SideA Current {_device.SideAPowerSupplier.PowerSupplierData.Current} is not in " + $"recipe Range {_powerSupplierStepPeriodDatas[_stepIndex].Current - currentErrorFault} ~ {_powerSupplierStepPeriodDatas[_stepIndex].Current + currentErrorFault}"); } if (!_isCurrentWarningA && ((_device.SideAPowerSupplier.PowerSupplierData.Current > _powerSupplierStepPeriodDatas[_stepIndex].Current + currentWarningFault && _device.SideAPowerSupplier.PowerSupplierData.Current < _powerSupplierStepPeriodDatas[_stepIndex].Current + currentErrorFault) || (_device.SideAPowerSupplier.PowerSupplierData.Current < _powerSupplierStepPeriodDatas[_stepIndex].Current - currentWarningFault && _device.SideAPowerSupplier.PowerSupplierData.Current > _powerSupplierStepPeriodDatas[_stepIndex].Current - currentErrorFault))) { _isCurrentWarningA = true; LOG.WriteLog(eEvent.WARN_METAL, Module, $"Current SideA Current {_device.SideAPowerSupplier.PowerSupplierData.Current} is not in " + $"recipe Range {_powerSupplierStepPeriodDatas[_stepIndex].Current - currentWarningFault} ~ {_powerSupplierStepPeriodDatas[_stepIndex].Current + currentWarningFault}"); } //监控电压B面 if (_device.SideBPowerSupplier.PowerSupplierData.Voltage > _recipe.VoltageWarningLevel && _device.SideBPowerSupplier.PowerSupplierData.Voltage < _recipe.VolatageLimitMax && !_isVotlageWarningB) { _isVotlageWarningB = true; LOG.WriteLog(eEvent.WARN_METAL, Module, $"Current SideB Votlage {_device.SideBPowerSupplier.PowerSupplierData.Voltage} is large than recipe VolatageWarningLevel {_recipe.VoltageWarningLevel}"); } if (_device.SideBPowerSupplier.PowerSupplierData.Voltage < _recipe.VolatageLimitMin) { if (metalEntity != null) { metalEntity.PostMsg(MetalMsg.Error); } Abort(); LOG.WriteLog(eEvent.ERR_METAL, Module, $"Current SideB Votlage {_device.SideBPowerSupplier.PowerSupplierData.Voltage} is lower than recipe VolatageLimitMin {_recipe.VolatageLimitMin}"); } if (_device.SideBPowerSupplier.PowerSupplierData.Voltage > _recipe.VolatageLimitMax) { if (metalEntity != null) { metalEntity.PostMsg(MetalMsg.Error); } Abort(); LOG.WriteLog(eEvent.ERR_METAL, Module, $"Current SideB Votlage {_device.SideBPowerSupplier.PowerSupplierData.Voltage} is large than recipe VolatageLimitMin{_recipe.VolatageLimitMax}"); } //监控电流B面 if (_device.SideBPowerSupplier.PowerSupplierData.Current > _powerSupplierStepPeriodDatas[_stepIndex].Current + currentErrorFault || _device.SideBPowerSupplier.PowerSupplierData.Current < _powerSupplierStepPeriodDatas[_stepIndex].Current - currentErrorFault) { if (metalEntity != null) { metalEntity.PostMsg(MetalMsg.Error); } Abort(); LOG.WriteLog(eEvent.ERR_METAL, Module, $"Current SideB Current {_device.SideBPowerSupplier.PowerSupplierData.Current} is not in " + $"recipe Range {_powerSupplierStepPeriodDatas[_stepIndex].Current - currentErrorFault} ~ {_powerSupplierStepPeriodDatas[_stepIndex].Current + currentErrorFault}"); } if (!_isCurrentWarningB && ((_device.SideBPowerSupplier.PowerSupplierData.Current > _powerSupplierStepPeriodDatas[_stepIndex].Current + currentWarningFault && _device.SideBPowerSupplier.PowerSupplierData.Current < _powerSupplierStepPeriodDatas[_stepIndex].Current + currentErrorFault) || (_device.SideBPowerSupplier.PowerSupplierData.Current < _powerSupplierStepPeriodDatas[_stepIndex].Current - currentWarningFault && _device.SideBPowerSupplier.PowerSupplierData.Current > _powerSupplierStepPeriodDatas[_stepIndex].Current - currentErrorFault))) { _isCurrentWarningB = true; LOG.WriteLog(eEvent.WARN_METAL, Module, $"Current SideB Current {_device.SideBPowerSupplier.PowerSupplierData.Current} is not in " + $"recipe Range {_powerSupplierStepPeriodDatas[_stepIndex].Current - currentWarningFault} ~ {_powerSupplierStepPeriodDatas[_stepIndex].Current + currentWarningFault}"); } } else { if (_side == SIDE_A) { //监控电压A面 MetalEntity metalEntity = Singleton.Instance.GetModule(Module); if (_device.SideAPowerSupplier.PowerSupplierData.Voltage > _recipe.VoltageWarningLevel && _device.SideAPowerSupplier.PowerSupplierData.Voltage < _recipe.VolatageLimitMax && !_isVotlageWarningA) { _isVotlageWarningA = true; LOG.WriteLog(eEvent.WARN_METAL, Module, $"Current SideA Votlage {_device.SideAPowerSupplier.PowerSupplierData.Voltage} is large than recipe VolatageWarningLevel {_recipe.VoltageWarningLevel}"); } if (_device.SideAPowerSupplier.PowerSupplierData.Voltage < _recipe.VolatageLimitMin) { if (metalEntity != null) { metalEntity.PostMsg(MetalMsg.Error); } Abort(); LOG.WriteLog(eEvent.ERR_METAL, Module, $"Current SideA Votlage {_device.SideAPowerSupplier.PowerSupplierData.Voltage} is lower than recipe VolatageLimitMin {_recipe.VolatageLimitMin}"); } if (_device.SideAPowerSupplier.PowerSupplierData.Voltage > _recipe.VolatageLimitMax) { if (metalEntity != null) { metalEntity.PostMsg(MetalMsg.Error); } Abort(); LOG.WriteLog(eEvent.ERR_METAL, Module, $"Current SideA Votlage {_device.SideAPowerSupplier.PowerSupplierData.Voltage} is large than recipe VolatageLimitMin {_recipe.VolatageLimitMax}"); } //监控电流A面 double currentWarningFault = _powerSupplierStepPeriodDatas[_stepIndex].Current * _recipe.CurrentWarningLevel * 0.01; double currentErrorFault = _powerSupplierStepPeriodDatas[_stepIndex].Current * _recipe.FaultPercent * 0.01; if (_device.SideAPowerSupplier.PowerSupplierData.Current > _powerSupplierStepPeriodDatas[_stepIndex].Current + currentErrorFault || _device.SideAPowerSupplier.PowerSupplierData.Current < _powerSupplierStepPeriodDatas[_stepIndex].Current - currentErrorFault) { if (metalEntity != null) { metalEntity.PostMsg(MetalMsg.Error); } Abort(); LOG.WriteLog(eEvent.ERR_METAL, Module, $"Current SideA Current {_device.SideAPowerSupplier.PowerSupplierData.Current} is not in " + $"recipe Range {_powerSupplierStepPeriodDatas[_stepIndex].Current - currentErrorFault} ~ {_powerSupplierStepPeriodDatas[_stepIndex].Current + currentErrorFault}"); } if (!_isCurrentWarningA && ((_device.SideAPowerSupplier.PowerSupplierData.Current > _powerSupplierStepPeriodDatas[_stepIndex].Current + currentWarningFault && _device.SideAPowerSupplier.PowerSupplierData.Current < _powerSupplierStepPeriodDatas[_stepIndex].Current + currentErrorFault) || (_device.SideAPowerSupplier.PowerSupplierData.Current < _powerSupplierStepPeriodDatas[_stepIndex].Current - currentWarningFault && _device.SideAPowerSupplier.PowerSupplierData.Current > _powerSupplierStepPeriodDatas[_stepIndex].Current - currentErrorFault))) { _isCurrentWarningA = true; LOG.WriteLog(eEvent.WARN_METAL, Module, $"Current SideA Current {_device.SideAPowerSupplier.PowerSupplierData.Current} is not in " + $"recipe Range{_powerSupplierStepPeriodDatas[_stepIndex].Current - currentWarningFault} ~ {_powerSupplierStepPeriodDatas[_stepIndex].Current + currentWarningFault}"); } } else { //监控电压B面 MetalEntity metalEntity = Singleton.Instance.GetModule(Module); if (!_isVotlageWarningB && _device.SideBPowerSupplier.PowerSupplierData.Voltage > _recipe.VoltageWarningLevel && _device.SideBPowerSupplier.PowerSupplierData.Voltage < _recipe.VolatageLimitMax) { _isVotlageWarningB = true; LOG.WriteLog(eEvent.WARN_METAL, Module, $"Current SideB Votlage {_device.SideBPowerSupplier.PowerSupplierData.Voltage} is large than recipe VolatageWarningLevel {_recipe.VoltageWarningLevel}"); } if (_device.SideBPowerSupplier.PowerSupplierData.Voltage < _recipe.VolatageLimitMin) { if (metalEntity != null) { metalEntity.PostMsg(MetalMsg.Error); } Abort(); LOG.WriteLog(eEvent.ERR_METAL, Module, $"Current SideB Votlage {_device.SideBPowerSupplier.PowerSupplierData.Voltage} is lower than recipe VolatageLimitMin {_recipe.VolatageLimitMin}"); } if (_device.SideBPowerSupplier.PowerSupplierData.Voltage > _recipe.VolatageLimitMax) { if (metalEntity != null) { metalEntity.PostMsg(MetalMsg.Error); } Abort(); LOG.WriteLog(eEvent.ERR_METAL, Module, $"Current SideB Votlage {_device.SideBPowerSupplier.PowerSupplierData.Voltage} is large than recipe VolatageLimitMin {_recipe.VolatageLimitMax}"); } //监控电流B面 double currentWarningFault = _powerSupplierStepPeriodDatas[_stepIndex].Current * _recipe.CurrentWarningLevel * 0.01; double currentErrorFault = _powerSupplierStepPeriodDatas[_stepIndex].Current * _recipe.FaultPercent * 0.01; if (_device.SideBPowerSupplier.PowerSupplierData.Current > _powerSupplierStepPeriodDatas[_stepIndex].Current + currentErrorFault || _device.SideBPowerSupplier.PowerSupplierData.Current < _powerSupplierStepPeriodDatas[_stepIndex].Current - currentErrorFault) { if (metalEntity != null) { metalEntity.PostMsg(MetalMsg.Error); } Abort(); LOG.WriteLog(eEvent.ERR_METAL, Module, $"Current SideB Current {_device.SideBPowerSupplier.PowerSupplierData.Current} is not in " + $"recipe Range{_powerSupplierStepPeriodDatas[_stepIndex].Current - currentErrorFault} ~ {_powerSupplierStepPeriodDatas[_stepIndex].Current + currentErrorFault}"); } if (!_isCurrentWarningB && ((_device.SideBPowerSupplier.PowerSupplierData.Current > _powerSupplierStepPeriodDatas[_stepIndex].Current + currentWarningFault && _device.SideBPowerSupplier.PowerSupplierData.Current < _powerSupplierStepPeriodDatas[_stepIndex].Current + currentErrorFault) || (_device.SideBPowerSupplier.PowerSupplierData.Current < _powerSupplierStepPeriodDatas[_stepIndex].Current - currentWarningFault && _device.SideBPowerSupplier.PowerSupplierData.Current > _powerSupplierStepPeriodDatas[_stepIndex].Current - currentErrorFault))) { _isCurrentWarningB = true; LOG.WriteLog(eEvent.WARN_METAL, Module, $"Current SideB Current {_device.SideBPowerSupplier.PowerSupplierData.Current} is not in " + $"recipe Range {_powerSupplierStepPeriodDatas[_stepIndex].Current - currentWarningFault} ~ {_powerSupplierStepPeriodDatas[_stepIndex].Current + currentWarningFault}"); } } } } /// /// 检验电流和电压合理性 /// /// /// /// /// private bool CheckSidePowerInvalid(double current,double voltage,string side) { double maxVoltage = _recipe.VolatageLimitMax; double warnVoltage = _recipe.VoltageWarningLevel; double minVoltage=_recipe.VolatageLimitMin; if (voltage > maxVoltage) { LOG.WriteLog(eEvent.ERR_METAL, Module, $"{side} voltage {voltage} is large than recipe max voltage {maxVoltage}"); return true; } if (voltage < minVoltage) { LOG.WriteLog(eEvent.ERR_METAL, Module, $"{side} voltage {voltage} is less than recipe min voltage {minVoltage}"); return true; } if (voltage > warnVoltage) { string str = $"{side} voltage is {voltage} in warning"; if (AlarmListManager.Instance.AddWarn(Module, $"{side} voltage", str)) { LOG.WriteLog(eEvent.WARN_PREWET, Module, str); } } double maxErrorCurrent = _powerSupplierStepPeriodDatas[_stepIndex].Current * (double)(1 + _recipe.FaultPercent * 0.01); double minErrorCurrent = _powerSupplierStepPeriodDatas[_stepIndex].Current * (double)(1 - _recipe.FaultPercent * 0.01); double maxWarnCurrent = _powerSupplierStepPeriodDatas[_stepIndex].Current * (double)(1 + _recipe.CurrentWarningLevel * 0.01); double minWarnCurrent = _powerSupplierStepPeriodDatas[_stepIndex].Current * (double)(1 - _recipe.CurrentWarningLevel * 0.01); if (current > maxErrorCurrent) { LOG.WriteLog(eEvent.ERR_METAL, Module, $"{side} current {current} is large than recipe max current {maxErrorCurrent}"); return true; } if (current < minErrorCurrent) { LOG.WriteLog(eEvent.ERR_METAL, Module, $"{side} current {current} is less than recipe min current {minErrorCurrent}"); return true; } if ((current <= maxErrorCurrent && current >= maxWarnCurrent) || (current >= minErrorCurrent && current <= minWarnCurrent)) { string str = $"{side} current {current} is in warning"; if (AlarmListManager.Instance.AddWarn(Module, $"{side} current", str)) { LOG.WriteLog(eEvent.WARN_PREWET, Module, str); } } return false; } /// /// 停止Linmot /// /// private bool StopLinmot() { return _device.StopLinmot(); } /// /// 停止电源 /// /// private bool StopPowerSupplier() { if (string.IsNullOrEmpty(_side)) { _device.SideAPowerSupplier.DisableOperation("", null); _device.SideBPowerSupplier.DisableOperation("", null); } else { CellPowerSupplier cellPowerSupplier = GetSidePowerSupplier(); cellPowerSupplier.DisableOperation("", null); } return true; } /// /// 切换成正常模式 /// /// private bool SwitchToNormal() { if (string.IsNullOrEmpty(_side)) { _device.SideAPowerSupplier.SwitchPowerRunModel((int)PowerRunModelEnum.Normal); _device.SideBPowerSupplier.SwitchPowerRunModel((int)PowerRunModelEnum.Normal); } else { CellPowerSupplier cellPowerSupplier = GetSidePowerSupplier(); cellPowerSupplier.SwitchPowerRunModel((int)PowerRunModelEnum.Normal); } return true; } /// /// 启动 /// /// /// public RState Start(params object[] objs) { _isVotlageWarningA = false; _isVotlageWarningB = false; _isCurrentWarningA = false; _isCurrentWarningB = false; _recipe = objs[0] as DepRecipe; if (_recipe == null) { LOG.WriteLog(eEvent.ERR_METAL, Module, "recipe is null"); return RState.Failed; } if (objs.Length > 1) { _side = objs[1].ToString(); } _startRecipeStep = false; _anodeAUsage = 0; _anodeBUsage = 0; _device = DEVICE.GetDevice(Module); _metalEntity = Singleton.Instance.GetModule(Module); UpdatePowerStepDatas(); _stepIndex = 0; _header.SoftWareVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); _header.Recipe = $"{_recipe.Ppid}.dep.rcp"; if(SC.ContainsItem("System.ToolID")) _header.ToolID = SC.GetStringValue("System.ToolID"); //lotTract记录SequenceRecipe MetalEntity metalEntity = Singleton.Instance.GetModule(Module); if (metalEntity.WaferHolderInfo != null && metalEntity.WaferHolderInfo.SequenceRecipe != null && !String.IsNullOrEmpty(metalEntity.WaferHolderInfo.SequenceRecipe.Ppid.ToString())) { _header.SequenceRecipe = metalEntity.WaferHolderInfo.SequenceRecipe.Ppid.ToString(); _header.ProcessTransferList = new List(); _header.ProcessTransferList.AddRange(metalEntity.WaferHolderInfo.SchedulerModules); metalEntity.WaferHolderInfo.SchedulerModules.Clear(); } _facilities = DEVICE.GetDevice("System.Facilities"); if (_facilities == null) { LOG.WriteLog(eEvent.ERR_METAL, Module, "Facility is null"); return RState.Failed; } string reservoirName = ReservoirItemManager.Instance.GetReservoirByMetal(Module); MetalItem metalItem = MetalItemManager.Instance.GetMetalItem(Module); _metalType = metalItem.SubType; if (_metalType == STRATUS) { _standardHotReservoirDevice = DEVICE.GetDevice(reservoirName); _standardHotMetalDevice = DEVICE.GetDevice(Module); if (_standardHotReservoirDevice == null || _standardHotMetalDevice == null) { LOG.WriteLog(eEvent.ERR_METAL, Module, $"metal or reservoir device is null"); return RState.Failed; } } else { _compactMembranReservoirDevice = DEVICE.GetDevice(reservoirName); _compactMembranMetalDevice = DEVICE.GetDevice(Module); if(_compactMembranMetalDevice == null || _compactMembranReservoirDevice == null) { LOG.WriteLog(eEvent.ERR_METAL, Module, $"metal or reservoir device is null"); return RState.Failed; } } ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(reservoirName); _temperatureController = DEVICE.GetDevice(reservoirItem.TCID); if (_temperatureController == null) { LOG.WriteLog(eEvent.ERR_METAL, Module, $"Temperature controller is null"); return RState.Failed; } _persistentValue = MetalPersistentManager.Instance.GetMetalPersistentValue(Module); if (_persistentValue == null) { LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} Persistent Value Object is not exist"); return RState.Failed; } _datas.Clear(); _lotTackTime = DateTime.Now; return Runner.Start(Module, "Metal run recipe"); } } }