using Aitex.Core.RT.Device; using Aitex.Core.RT.Log; using Aitex.Core.RT.Routine; using Aitex.Core.Util; using MECF.Framework.Common.Beckhoff.AxisProvider; using MECF.Framework.Common.Beckhoff.Station; using MECF.Framework.Common.CommonData.PowerSupplier; using MECF.Framework.Common.RecipeCenter; using MECF.Framework.Common.Routine; using MECF.Framework.Common.SubstrateTrackings; using MECF.Framework.Common.ToolLayout; using MECF.Framework.Common.Utilities; using PunkHPX8_Core; using PunkHPX8_RT.Devices.AXIS; using PunkHPX8_RT.Devices.Facilities; using PunkHPX8_RT.Devices.PlatingCell; using PunkHPX8_RT.Devices.PowerSupplier; using PunkHPX8_RT.Modules.Reservoir; using SecsGem.Core.ItemModel; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using static Mono.Security.X509.X520; namespace PunkHPX8_RT.Modules.PlatingCell { public class PlatingCellRunRecipeRoutine : RoutineBase, IRoutine { private enum RunRecipeStep { Delay, VerticalGotoRinse, CheckVerticalGotoRinse, InterRinse, CheckInterRinse, RotationStartEntry, RotationChangeToEntrySpeed, AngleTilt, VerticalGotoPlate, WaitEntryCurrentProtectedFromRinse, WaitEntryCurrentProtectedFromHome, RunPowerStepWithEntryProtect, CheckVerticalGotoPlate, AngleVertical, WaitPlatingDelay, RunPowerStep, RunPowerStepWait, End } #region 常量 private const int ALL_DAY_MILLOSECONDS = 24 * 60 * 60 * 1000; /// /// ROTATION电机转速比例 /// private const int SPEED_RATIO = 1; #endregion #region 内部变量 /// /// recipe /// private DepRecipe _recipe; /// /// Platingcell device /// private PlatingCellDevice _device; /// /// Rotation axis /// private JetAxisBase _rotationAxis; /// ///rotation Provider对象 /// private BeckhoffProviderAxis _rotationProviderAxis; /// cycle次数 /// private int _cycle; /// /// 当前完成的Cycle次数 /// private int _currentCycle; /// /// platingcell entity /// private PlatingCellEntity _platingCellEntity; /// /// 对应reservoir entity /// private ReservoirEntity _reservoirEntity; /// /// interbal rinse routien /// private PlatingCellInterRinseRoutine _interRinseRoutine; /// /// vertical axis entity /// private PlatingCellVerticalEntity _verticalEntity; /// /// vertical 轴的位置数据 /// private BeckhoffStationAxis _verticalBeckhoffStation; /// /// recipe中是否存在电机反转 /// private bool _isRecipeContainsRevserseRotation = false; /// /// run recipe 过程中电机会停的位置(需要根据recipe计算出来) /// private List _targetPositionList = new List(); #region 电镀通电相关 /// /// PlatingDelay计时 /// private DateTime _hotPlatingRunTime; /// /// 不通电 /// private bool _isZeroCurrent = false; /// /// 通电时长 /// private int _totalMicrosecond = 0; /// /// Power 集合 /// List _powerSupplierStepPeriodDatas = new List(); /// /// 记录dep recipe中出现的最大电流,用于启动电源前切换挡位 /// private double _maxCurrent = 0; #endregion #endregion /// /// 构造函数 /// /// public PlatingCellRunRecipeRoutine(string module) : base(module) { _interRinseRoutine = new PlatingCellInterRinseRoutine(module); } /// /// 中止 /// public void Abort() { Runner.Stop("Manual Abort"); } /// /// 监控 /// /// public RState Monitor() { //vertical去rinse位置 Runner.RunIf(RunRecipeStep.VerticalGotoRinse, _recipe.RinseBeforeEntryEnable, () => { return StartVertical("Rinse", _recipe.IntervalRinseZoffset); }, _delay_1ms) .WaitWithStopConditionIf(RunRecipeStep.CheckVerticalGotoRinse, _recipe.RinseBeforeEntryEnable, CheckVerticalEnd, CheckVerticalError) //执行interval rinse .RunIf(RunRecipeStep.InterRinse, _recipe.RinseBeforeEntryEnable, () => { return _interRinseRoutine.Start(_recipe, _rotationAxis, _device, _rotationProviderAxis, _targetPositionList[0]) == RState.Running; }) .WaitWithStopConditionIf(RunRecipeStep.CheckInterRinse, _recipe.RinseBeforeEntryEnable, CheckInterRinseEndStatus, () => CommonFunction.CheckRoutineStopState(_interRinseRoutine)) //启动Rotation/Rotation 设置为entry 转速 (没有intercal rinse 在entry开始的时候启动rotation) .RunIf(RunRecipeStep.RotationStartEntry, !_recipe.RinseBeforeEntryEnable, RotationStartEntry, _delay_1ms) //有intercal rinse 直接变速 (rotation在interval rinse的时候已经启动了) .RunIf(RunRecipeStep.RotationChangeToEntrySpeed, _recipe.RinseBeforeEntryEnable, () => { return ChangeSpeed(_recipe.EntrySpinSpeed); }, _delay_1ms) //Angle tilt 操作 .Run(RunRecipeStep.AngleTilt, _device.HeadtTiltAction, () => { return _device.PlatingCellDeviceData.IsHeadTilt; }, _delay_1s) //vertical goto plate .Run(RunRecipeStep.VerticalGotoPlate, () => { return StartVertical("Plate", _recipe.EntryZoffset); }, _delay_1ms) //vertical 到达entry位置前110ms .DelayIf(RunRecipeStep.WaitEntryCurrentProtectedFromRinse, !_recipe.RinseBeforeEntryEnable, CalculateVerticaMoveTime("Rinse", "Entry") - 110 > 0 ? CalculateVerticaMoveTime("Rinse", "Entry") : 0) //提前110ms,多10ms .DelayIf(RunRecipeStep.WaitEntryCurrentProtectedFromHome, _recipe.RinseBeforeEntryEnable, CalculateVerticaMoveTime("Home", "Entry") - 110 > 0 ? CalculateVerticaMoveTime("Home", "Entry") : 0) //有上电保护,此刻给电 .RunIf(RunRecipeStep.RunPowerStepWithEntryProtect, !_recipe.IsEntryTypeCold,StartPowerStep, _delay_1ms) //检查vertical到达plate位置 .WaitWithStopCondition(RunRecipeStep.CheckVerticalGotoPlate, CheckVerticalEnd, CheckVerticalError) //Angle vertical操作 .Run(RunRecipeStep.AngleVertical, _device.HeadtVerticalAction, () => { return _device.PlatingCellDeviceData.IsHeadVertical; }, _delay_1s) //如果不需要上电保护,执行plating delay .RunIf(RunRecipeStep.WaitPlatingDelay, _recipe.IsEntryTypeCold, NullFun, _recipe.PlatingDelay) //没有上电保护,此刻给电 .RunIf(RunRecipeStep.RunPowerStep, _recipe.IsEntryTypeCold, StartPowerStep, _delay_1ms) .WaitWithStopCondition(RunRecipeStep.RunPowerStepWait, CheckRecipeStepEndStatus, CheckRecipeStepStopStatus, _delay_1ms) .End(RunRecipeStep.End, NullFun); return Runner.Status; } /// /// 启动PowerSupplier /// /// private bool StartPowerStep() { bool result = _device.PowerSupplier.StartSetStepPeriodNoWaitEnd(_powerSupplierStepPeriodDatas); if (!result) { _device.PowerSupplier.DisableOperation("", null); return false; } return true; } /// /// 检验Powerstep是否启动完成 /// /// private bool CheckRecipeStepEndStatus() { if (_isZeroCurrent) { return true; } return _device.PowerSupplier.Status == RState.End; } /// /// 检验Powerstep是否启动失败 /// /// private bool CheckRecipeStepStopStatus() { if (_isZeroCurrent) { return false; } return _device.PowerSupplier.Status == RState.Failed || _device.PowerSupplier.Status == RState.Timeout; } /// /// 计算vertical 从一个位置移动到另一个位置用时 /// /// /// /// 返回值单位毫秒 private int CalculateVerticaMoveTime(string sourceLocation,string destinationLocation) { Station sourceStation = _verticalBeckhoffStation.Stations.FirstOrDefault(p => p.Name == $"{_verticalEntity.Module}.{sourceLocation}"); Station destinationStation = _verticalBeckhoffStation.Stations.FirstOrDefault(p => p.Name == $"{_verticalEntity.Module}.{destinationLocation}"); Double.TryParse(sourceStation.Position, out double sourcePosition); Double.TryParse(destinationStation.Position, out double destinationPosition); double time = Math.Abs(destinationPosition - sourcePosition) / _verticalEntity.MotionData.ProfileVelocity * 10; if (time <= 0) { NotifyError(eEvent.WARN_PLATINGCELL, "Calculate Vertica Move Time is 0", 0); return 0; } return (int)time; } /// /// change speed /// /// private bool ChangeSpeed(int speed) //参数speed单位是rmp { double realSpeed = BeckhoffVelocityUtil.ConvertVelocityToDegPerSecondByRPM(speed); bool result = _rotationAxis.ChangeSpeed((int)realSpeed); if (!result) { NotifyError(eEvent.ERR_PLATINGCELL, "Change Speed failed", 0); return false; } return true; } private bool CheckInterRinseEndStatus() { bool result = CommonFunction.CheckRoutineEndState(_interRinseRoutine); SubRoutineStep = _interRinseRoutine.CurrentStep; return result; } /// /// rotation 从entry步骤开始旋转 /// /// private bool RotationStartEntry() { bool result = _rotationAxis.ProfilePosition(_targetPositionList[0], _recipe.IntervalRinseSpeed * SPEED_RATIO, 0, 0); if (!result) { NotifyError(eEvent.ERR_PLATINGCELL, "Start Rotation is failed", 0); return false; } return true; } /// /// vertical 运行 /// /// 目标位置名称 /// 偏移量 /// private bool StartVertical(string positionName,double offset) { double position = 0; Station station = _verticalBeckhoffStation.Stations.FirstOrDefault(p => p.Name == $"{_verticalEntity.Module}.{positionName}"); if (station != null) { if(!Double.TryParse(station.Position, out position)) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "vertical station position is error"); return false; } } else { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, $"vertical station {_verticalEntity.Module}.{positionName} is null"); return false; } return _verticalEntity.CheckToPostMessage(Aitex.Core.RT.Log.eEvent.INFO_PLATINGCELL, Module, (int)PlatingCellVerticalEntity.VerticalMsg.Position, position + offset); } /// /// 检验垂直电机是否运动完成 /// /// private bool CheckVerticalEnd() { return _verticalEntity.IsIdle; } /// /// 检验垂直是否出现错误 /// /// private bool CheckVerticalError() { return _verticalEntity.IsError; } /// /// 启动 /// /// /// public RState Start(params object[] objs) { _recipe = objs[0] as DepRecipe; if (_recipe == null) { LOG.WriteLog(eEvent.ERR_METAL, Module, "recipe is null"); return RState.Failed; } if (objs.Length > 1) { _cycle = (int)objs[1]; } _device = DEVICE.GetDevice(Module); _rotationAxis = DEVICE.GetDevice($"{Module}.Rotation"); _rotationProviderAxis = BeckhoffAxisProviderManager.Instance.GetAxisProvider($"{Module}.Rotation"); if (_rotationProviderAxis == null) { NotifyError(eEvent.ERR_PLATINGCELL, $"{Module}.Rotation Provider is not exist", 0); return RState.Failed; } //获取vertical entity string vertical = ModuleMatcherManager.Instance.GetPlatingVerticalByCell(Module); _verticalEntity = Singleton.Instance.GetModule(vertical); //获取platingcell eneity _platingCellEntity = Singleton.Instance.GetModule(Module); //获取对应reservoir eneity string reservoir = ReservoirItemManager.Instance.GetReservoirByPlatingCell(Module); _reservoirEntity = Singleton.Instance.GetModule(reservoir); //获取vertical station信息对象 _verticalBeckhoffStation = BeckhoffStationLocationManager.Instance.GetStationAxis($"{_verticalEntity.Module}", "Vertical"); if (!CheckPreCondition()) { return RState.Failed; } CalculatVerticalPosition(_recipe); UpdatePowerStepDatas(); _currentCycle = 0; return Runner.Start(Module, "Run Recipe"); } /// /// 初始化Power step步骤 /// private bool UpdatePowerStepDatas() { _isZeroCurrent = false; double current = 0; _totalMicrosecond = 0; _powerSupplierStepPeriodDatas.Clear(); if (!_recipe.IsEntryTypeCold)//如果entry带上电保护,那么把上电保护的部分加到步阶电流的第一步 { PowerSupplierStepPeriodData step = new PowerSupplierStepPeriodData(); //上电保护时间(提前上电时间+延迟时间) step.Hour = 0; step.Minute = (ushort)((_recipe.PlatingDelay) / 60); step.Second = (ushort)(_recipe.PlatingDelay % 60); step.Microsecond = 110; //到达entry位置提前100ms上电,提前一丢丢 step.Voltage = _recipe.DepMaxVoltageWarning; _powerSupplierStepPeriodDatas.Add(step); } foreach (var item in _recipe.DepSteps) { PowerSupplierStepPeriodData step = new PowerSupplierStepPeriodData(); step.Current = item.CurrentValue; if(item.CurrentValue > _maxCurrent) { _maxCurrent = item.CurrentValue; } step.Hour = (ushort)(item.DurartionSeconds / 3600); step.Minute = (ushort)((item.DurartionSeconds - step.Hour * 3600) / 60); step.Second = (ushort)(item.DurartionSeconds % 60); step.Microsecond = 0; step.Voltage = _recipe.DepMaxVoltageWarning; _powerSupplierStepPeriodDatas.Add(step); current += step.Current; _totalMicrosecond += item.DurartionSeconds * 1000; } if (current == 0) { _isZeroCurrent = true; } if(_maxCurrent > 0.6) //设置电源挡位,有超过0.6A的电流则用高档,否则用中挡 { return _device.PowerSupplier.SetPowerGrade("set power grade high", new object[] { 2 }); } else { return _device.PowerSupplier.SetPowerGrade("set power grade high", new object[] { 1 }); } } /// /// 根据dep recipe计算出整个电镀过程中Rotation profile position可能去到的位置 /// private void CalculatVerticalPosition(DepRecipe recipe) { foreach(var item in recipe.DepSteps) { if (item.BiDireaction) { _isRecipeContainsRevserseRotation = true; } else { continue; } } //不存在双向旋转直接给roation一个很大的值,电镀完成后再停止 if (!_isRecipeContainsRevserseRotation) { _targetPositionList.Add(12 * 60 * 60 * 500 * 6); //以500rmp 运行12个小时的位置 } else { } } /// /// 检验前置条件 /// /// private bool CheckPreCondition() { if (_recipe == null) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "Recipe is null"); return false; } //if (_recipe.DepSteps.Count == 0) //{ // LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "Recipe DepSteps count is 0"); // return false; //} CheckAxisHome(); CheckFacility(); CheckModuleAndReservoir(); return true; } private bool CheckModuleAndReservoir() { if (!_platingCellEntity.IsIdle) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, $"{Module} is not initialized"); return false; } if (!_reservoirEntity.IsIdle) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, $"Releated reseroivr is not initialized"); return false; } if (!_reservoirEntity.TemperatureReached) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, $"Releated reseroivr temperature is not reached"); return false; } if ("Manual".Equals(_reservoirEntity.PersistentValue.OperatingMode) && !WaferManager.Instance.CheckHasWafer(Module, 0)) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, $"Run recipe in manual must has wafer!"); return false; } return true; } /// /// 检查马达是否上电且home /// /// private bool CheckAxisHome() { if (!_rotationAxis.IsSwitchOn) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "Rotation Axis is off"); return false; } if (!_rotationAxis.IsHomed) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "Rotation Axis is not home"); return false; } if (!_verticalEntity.IsIdle) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "Vertical Axis is not home"); return false; } return true; } /// /// 检查facility /// /// private bool CheckFacility() { SystemFacilities systemFacilities = DEVICE.GetDevice("System.Facilities"); if (systemFacilities == null) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "Facility is null"); return false; } if (!systemFacilities.CDAEnable) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "Facility CDA is off"); return false; } if (!systemFacilities.N2Enable) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "Facility N2 is off"); return false; } if (!systemFacilities.DIFillEnable) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "Facility DIW is off"); return false; } if (systemFacilities.FacilitiesDataDic["CDA1Pressure"].IsError || systemFacilities.FacilitiesDataDic["CDA2Pressure"].IsError) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "Facility CDA Data is in errro range"); return false; } if (systemFacilities.FacilitiesDataDic["Nitrogen1APressure"].IsError || systemFacilities.FacilitiesDataDic["Nitrogen1BPressure"].IsError || systemFacilities.FacilitiesDataDic["Nitrogen2APressure"].IsError || systemFacilities.FacilitiesDataDic["Nitrogen2BPressure"].IsError) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "Facility N2 Data is in errro range"); return false; } if (systemFacilities.FacilitiesDataDic["DiWaterPressure"].IsError) { LOG.WriteLog(eEvent.ERR_PLATINGCELL, Module, "Facility Diw Pressure value is in errro range"); return false; } return true; } } }