| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088 | 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 内部变量        /// <summary>        /// recipe        /// </summary>        private DepRecipe _recipe;        /// <summary>        /// Plate Delay时间        /// </summary>        private DateTime _platingDelayTime = DateTime.Now;        /// <summary>        /// 启动步骤时间        /// </summary>        private DateTime _startStepTime = DateTime.Now;        /// <summary>        /// 电量步骤计数        /// </summary>        private Stopwatch _stepWatch = new Stopwatch();        /// <summary>        /// Power 集合        /// </summary>        List<PowerSupplierStepPeriodData> _powerSupplierStepPeriodDatas = new List<PowerSupplierStepPeriodData>();        /// <summary>        /// 单面        /// </summary>        private string _side;        /// <summary>        /// 步骤索引        /// </summary>        private int _stepIndex = 0;        /// <summary>        /// 设备        /// </summary>        private MetalCellDevice _device;        /// <summary>        /// 设备Entity        /// </summary>        private MetalEntity _metalEntity;        /// <summary>        /// A面电量        /// </summary>        private double _anodeAUsage;        /// <summary>        /// B面电量        /// </summary>        private double _anodeBUsage;        /// <summary>        /// 是否启动recipe步骤        /// </summary>        private bool _startRecipeStep = false;        /// <summary>        /// lock track time        /// </summary>        private DateTime _lotTackTime = DateTime.Now;        /// <summary>        /// LotTrack数据        /// </summary>        private List<MetalLotTrackData> _datas = new List<MetalLotTrackData>();        /// <summary>        /// LotTrack文件头数据        /// </summary>        private LotTrackFileHeaderCommonData _header = new LotTrackFileHeaderCommonData();        /// <summary>        /// Facilities        /// </summary>        private SystemFacilities _facilities;        /// <summary>        /// StandardHot Reservoir Device        /// </summary>        private StandardHotReservoirDevice _standardHotReservoirDevice;        /// <summary>        /// StandardHot Metal Device        /// </summary>        private StandardHotMetalDevice _standardHotMetalDevice;        /// <summary>        /// CompactMembran Reservoir Device        /// </summary>        private CompactMembranReservoirDevice _compactMembranReservoirDevice;        /// <summary>        /// CompactMembran Reservoir Metal        /// </summary>        private CompactMembranMetalDevice _compactMembranMetalDevice;        /// <summary>        /// MetalType        /// </summary>        private string _metalType;        /// <summary>        /// TC device        /// </summary>        private TemperatureController _temperatureController;        /// <summary>        /// Persistent value        /// </summary>        private MetalPersistentValue _persistentValue;        /// <summary>        /// PlatingDelay计时        /// </summary>        private DateTime _hotPlatingRunTime;        /// <summary>        /// 是否处于warning状态        /// </summary>        private bool _isVotlageWarningA = false;        private bool _isVotlageWarningB = false;        private bool _isCurrentWarningA = false;        private bool _isCurrentWarningB = false;        #endregion        #region 属性        /// <summary>        /// A面电量        /// </summary>        public double AnodeAUsage { get { return _anodeAUsage; } }        /// <summary>        /// B面电量        /// </summary>        public double AnodeBUsage { get { return _anodeBUsage; } }        /// <summary>        /// LotTrack数据        /// </summary>        public List<MetalLotTrackData> MetalLotTrackDatas { get { return _datas; } }        /// <summary>        /// LotTrack文件头数据        /// </summary>        public LotTrackFileHeaderCommonData MetalLotTrackHeaderDatas { get { return _header; } }        #endregion        /// <summary>        /// 构造函数        /// </summary>        public ReservoirRunRecipeRoutine(string moduleName):base(moduleName)        {        }        /// <summary>        /// 中止        /// </summary>        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");        }        /// <summary>        /// 监控        /// </summary>        /// <returns></returns>        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;        }        /// <summary>        /// 记录Lottrack        /// </summary>        private void LottrackRecord()        {            //记录Lottrack            if (DateTime.Now.Subtract(_lotTackTime).TotalMilliseconds >= LOTTRACK_TIME)            {                AddLotTrackData();                _lotTackTime = DateTime.Now;            }        }        /// <summary>        /// 获取Lot Track数据        /// </summary>        /// <returns></returns>        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);        }        /// <summary>        /// Plate Delay        /// </summary>        /// <returns></returns>        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;        }        /// <summary>        /// Hot Plating        /// </summary>        /// <returns></returns>        private bool HotPlating()        {            UpdateHotPlatingStepDatas();            return StartPowerStep();        }        /// <summary>        /// 启动PowerSupplier        /// </summary>        /// <returns></returns>        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;            }        }        /// <summary>        /// 检验Powerstep是否启动完成        /// </summary>        /// <returns></returns>        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;        }        /// <summary>        /// 检验Powerstep是否启动完成        /// </summary>        /// <returns></returns>        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;        }        /// <summary>        /// 获取单面PowerSupplier        /// </summary>        /// <returns></returns>        private CellPowerSupplier GetSidePowerSupplier()        {            if (_side == SIDE_A)            {                return _device.SideAPowerSupplier;            }            else            {                return _device.SideBPowerSupplier;            }        }        /// <summary>        /// 启动        /// </summary>        /// <returns></returns>        private bool StartPowerStep(CellPowerSupplier cellPowerSupplier)        {            bool result = cellPowerSupplier.StartSetStepPeriodNoWaitEnd(_powerSupplierStepPeriodDatas);            if (!result)            {                cellPowerSupplier.DisableOperation("", null);                return false;            }            return true;        }        /// <summary>        /// 更新HotPlating step数据        /// </summary>        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);            }        }        /// <summary>        /// 更新Power step步骤        /// </summary>        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);            }        }        /// <summary>        /// 等待Plating Delay结束        /// </summary>        /// <returns></returns>        private bool WaitPlatingDelay()        {            if (DateTime.Now.Subtract(_platingDelayTime).TotalSeconds >= _recipe.PlatingDelaySeconds)            {                return true;            }            return false;        }        /// <summary>        /// 检验步骤是否完成        /// </summary>        /// <returns></returns>        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;        }        /// <summary>        /// 检验Power是否Disable        /// </summary>        /// <returns></returns>        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;        }        /// <summary>        /// 检验电压电流的合理性        /// </summary>        /// <returns></returns>        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;        }        /// <summary>        /// 监控电流和电压        /// </summary>        private void CheckVotlageAndCurrent()        {            if (string.IsNullOrEmpty(_side))            {                //监控电压A面                MetalEntity metalEntity = Singleton<RouteManager>.Instance.GetModule<MetalEntity>(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<RouteManager>.Instance.GetModule<MetalEntity>(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<RouteManager>.Instance.GetModule<MetalEntity>(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}");                    }                }            }        }        /// <summary>        /// 检验电流和电压合理性        /// </summary>        /// <param name="current"></param>        /// <param name="voltage"></param>        /// <param name="side"></param>        /// <returns></returns>        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;        }        /// <summary>        /// 停止Linmot        /// </summary>        /// <returns></returns>        private bool StopLinmot()        {            return _device.StopLinmot();        }        /// <summary>        /// 停止电源        /// </summary>        /// <returns></returns>        private bool StopPowerSupplier()        {            if (string.IsNullOrEmpty(_side))            {                _device.SideAPowerSupplier.DisableOperation("", null);                _device.SideBPowerSupplier.DisableOperation("", null);            }            else            {                CellPowerSupplier cellPowerSupplier = GetSidePowerSupplier();                cellPowerSupplier.DisableOperation("", null);            }            return true;        }        /// <summary>        /// 切换成正常模式        /// </summary>        /// <returns></returns>        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;        }        /// <summary>        /// 启动        /// </summary>        /// <param name="objs"></param>        /// <returns></returns>        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<MetalCellDevice>(Module);            _metalEntity = Singleton<RouteManager>.Instance.GetModule<MetalEntity>(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<RouteManager>.Instance.GetModule<MetalEntity>(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<string>();                _header.ProcessTransferList.AddRange(metalEntity.WaferHolderInfo.SchedulerModules);                metalEntity.WaferHolderInfo.SchedulerModules.Clear();            }            _facilities = DEVICE.GetDevice<SystemFacilities>("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<StandardHotReservoirDevice>(reservoirName);                              _standardHotMetalDevice = DEVICE.GetDevice<StandardHotMetalDevice>(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<CompactMembranReservoirDevice>(reservoirName);                _compactMembranMetalDevice = DEVICE.GetDevice<CompactMembranMetalDevice>(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<TemperatureController>(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");        }    }}
 |