| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342 | using Aitex.Core.RT.DataCenter;using Aitex.Core.RT.Fsm;using Aitex.Core.RT.Log;using Aitex.Core.Util;using Aitex.Core.Utilities;using MECF.Framework.Common.Equipment;using MECF.Framework.Common.WaferHolder;using System;using MECF.Framework.Common.ToolLayout;using MECF.Framework.Common.Persistent.Reservoirs;using Aitex.Core.RT.OperationCenter;using MECF.Framework.Common.RecipeCenter;using Aitex.Core.RT.RecipeCenter;using MECF.Framework.Common.SubstrateTrackings;using Aitex.Core.RT.Device;using MECF.Framework.Common.Routine;using MECF.Framework.Common.ProcessCell;using Aitex.Core.RT.SCCore;using MECF.Framework.Common.Alarm;using CyberX8_Core;using CyberX8_RT.Devices.Metal;using CyberX8_RT.Devices.Reservoir;using CyberX8_RT.Modules.Metal;using CyberX8_RT.Modules.Reservoir;using CyberX8_RT.Modules;using CyberX8_RT.Modules.EFEM;using static Mono.Security.X509.X520;namespace CyberX8_RT.Modules.Metal{    public class MetalEntity : Entity, IEntity, IModuleEntity    {        #region 常量        private const string STRATUS = "Stratus";        private const string AUTO = "Auto";        private const string MANUAL = "Manual";        private const string DISABLED = "Disabled";        private const string ENGINEERING = "Engineering";        private const string PRODUCTION = "Production";        #endregion        #region 内部变量        /// <summary>        /// 已经完成的Cycle次数        /// </summary>        private int _achievedCycle = 0;        /// <summary>        /// Metal项        /// </summary>        private MetalItem _metalItem;        /// <summary>        /// 持久化对象        /// </summary>        private MetalPersistentValue _persistentValue;        /// <summary>        /// c&m Initialize routine        /// </summary>        private CompactEmbranceInitializeRoutine _compactEmbranceInitializeRoutine;        /// <summary>        /// C&M Runrecipe routine        /// </summary>        private CompactEmbranceRunRecipeRoutine _compactEmbranceRunRecipeRoutine;        /// <summary>        /// S&H Initialize Routine        /// </summary>        private StandardHotInitializeRoutine _standardHotInitializeRoutine;        /// <summary>        /// S&H RunRecipe Routine        /// </summary>        private StandardHotRunRecipeRoutine _standardHotRunRecipeRoutine;        /// <summary>        /// Current short test routine        /// </summary>        private CurrentShortTestRoutine _currentShortTestRoutine;        /// <summary>        /// 当前Recipe        /// </summary>        private DepRecipe _currentRecipe = null;        /// <summary>        /// recipe时间        /// </summary>        private int _recipeTime;        /// <summary>        /// 循环次数        /// </summary>        private int _cycle = 0;        /// <summary>        /// 运行Recipe面        /// </summary>        private string _recipeSide = "";        /// <summary>        /// 当前RunRecipe Routine        /// </summary>        private RoutineBase _currentRunRecipeRoutine;        /// <summary>        /// Runrecipe已经运行的时间        /// </summary>        private int _runrecipeElapsedTime = 0;        /// <summary>        /// run recipe start time        /// </summary>        private DateTime _runRecipeStartTime;        /// <summary>        ///  run recipe complete time        /// </summary>        private DateTime _runRecipeCompleteTime;        /// <summary>        /// 所在reservoir        /// </summary>        private StandardHotReservoirDevice _reservoirDevice;        /// <summary>        /// 所在reservoir名字        /// </summary>        private string _reservoirName;        /// <summary>        /// 当前完成recipe次数        /// </summary>        private int _currentCycleTimes;        /// <summary>        /// 当前recipe是否完成        /// </summary>        private bool _isCurrentReceipeComplete;        /// <summary>        ///  当前wafershuttle开始浸泡时间        /// </summary>        private DateTime _currentWaferShuttleStartSoakTime;        /// <summary>        /// Wafershuttle最大浸泡时间        /// </summary>        private int _waferShuttleSoakMaxTime;        #endregion        #region 属性        /// <summary>        /// 模块名称        /// </summary>        public ModuleName Module { get; private set; }        /// <summary>        /// 初始化状态        /// </summary>        public bool IsInit { get { return fsm.State == (int)MetalState.Init; } }        /// <summary>        /// 空闲状态        /// </summary>        public bool IsIdle { get { return fsm.State == (int)MetalState.Idle; } }        /// <summary>        /// 是否发生错误        /// </summary>        public bool IsError { get { return fsm.State == (int)MetalState.Error; } }        /// <summary>        /// 是否正在作业        /// </summary>        public bool IsBusy { get { return fsm.State > (int)MetalState.Idle; } }        /// <summary>        /// 是否初始化完成        /// </summary>        public bool IsInitialized { get { return fsm.State >= (int)MetalState.Initialized; } }        /// <summary>        /// 是否禁用        /// </summary>        public bool IsDisable { get { return _persistentValue == null || _persistentValue.OperatingMode == DISABLED; } }        /// <summary>        /// 自动模式        /// </summary>        public bool IsAuto { get { return _persistentValue != null && _persistentValue.OperatingMode == AUTO; } }        /// <summary>        /// 自动模式        /// </summary>        public bool IsManual { get { return _persistentValue != null && _persistentValue.OperatingMode == MANUAL; } }        /// <summary>        /// 是否为工程模式        /// </summary>        public bool IsEngineering { get { return _persistentValue != null && _persistentValue.RecipeOperatingMode == ENGINEERING; } }        /// <summary>        /// 是否为产品模式        /// </summary>        public bool IsProduction { get { return _persistentValue != null && _persistentValue.RecipeOperatingMode == PRODUCTION; } }        /// <summary>        /// WaferHolder信息        /// </summary>        public WaferHolderInfo WaferHolderInfo { get { return WaferHolderManager.Instance.GetWaferHolder(Module.ToString()); } }        /// <summary>        /// 已完成的RunRecipeCycle次数        /// </summary>        public int AchievedCycle { get { return _achievedCycle; } }        /// <summary>        /// 当前状态机状态        /// </summary>        public int State { get { return fsm.State; } }        /// <summary>        /// 当前Metal设置的WaferSize        /// </summary>        public int MetalWaferSize { get { return _persistentValue.MetalWaferSize; } }        /// <summary>        /// recipe时长        /// </summary>        public int RecipeTime { get { return _recipeTime; } }        /// <summary>        /// 剩余时间        /// </summary>        public override int TimeToReady        {            get            {                if (IsIdle)                {                    return 0;                }                switch (fsm.State)                {                    case (int)MetalState.RunReciping:                        return Math.Max(_recipeTime - fsm.ElapsedTime / 1000, 0);                    default:                        return 0;                }            }        }        /// <summary>        /// 用量        /// </summary>        public MetalUsage MetalUsage { get { return MetalUsageManager.Instance.GetMetalUsage(Module.ToString()); } }        #endregion        /// <summary>        /// 构造函数        /// </summary>        /// <param name="moduleName"></param>        public MetalEntity(ModuleName moduleName)        {            this.Module = moduleName;            WaferManager.Instance.SubscribeLocation(Module, 2);            InitialFsm();        }        /// <summary>        /// 初始化        /// </summary>        /// <returns></returns>        protected override bool Init()        {            InitializeDATA();            InitializeParameter();            InitializeRoutine();            InitializeOperation();            return true;        }        /// <summary>        /// 初始化参数        /// </summary>        private void InitializeParameter()        {            _persistentValue = MetalPersistentManager.Instance.GetMetalPersistentValue(Module.ToString());            if (_persistentValue == null)            {                LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), "Persistent Value Object is not exist");            }            _metalItem = MetalItemManager.Instance.GetMetalItem(Module.ToString());            _reservoirName = ReservoirItemManager.Instance.GetReservoirByMetal(Module.ToString());            _reservoirDevice = DEVICE.GetDevice<StandardHotReservoirDevice>($"{_reservoirName}");            _isCurrentReceipeComplete = false;            _waferShuttleSoakMaxTime = SC.GetValue<int>($"Metal.WaferShuttleSoakMaxTime");        }        /// <summary>        /// 初始化Routine        /// </summary>        private void InitializeRoutine()        {            _compactEmbranceInitializeRoutine = new CompactEmbranceInitializeRoutine(Module.ToString());            _standardHotInitializeRoutine = new StandardHotInitializeRoutine(Module.ToString());            _compactEmbranceRunRecipeRoutine = new CompactEmbranceRunRecipeRoutine(Module.ToString());            _standardHotRunRecipeRoutine = new StandardHotRunRecipeRoutine(Module.ToString());            _currentShortTestRoutine = new CurrentShortTestRoutine(Module.ToString());        }        /// <summary>        /// 初始化操作        /// </summary>        private void InitializeOperation()        {            OP.Subscribe($"{Module}.InitializeAll", (cmd, args) => { return CheckToPostMessage<MetalState, MetalMsg>(eEvent.ERR_METAL, Module.ToString(), (int)MetalMsg.Initialize); });            OP.Subscribe($"{Module}.CycleManualProcessRecipe", (cmd, args) =>            {                DepRecipe recipe = RecipeFileManager.Instance.LoadGenericityRecipe<DepRecipe>(args[0].ToString());                if (recipe == null)                {                    LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{args[0]} recipe is null");                    return false;                }                object[] objects = new object[args.Length];                objects[0] = recipe;                for (int i = 1; i < args.Length; i++)                {                    objects[i] = args[i];                }                return CheckToPostMessage<MetalState, MetalMsg>(eEvent.ERR_METAL, Module.ToString(), (int)MetalMsg.RunRecipe, objects);            });            OP.Subscribe($"{Module}.CurrentShortTest", (cmd, args) => { return CheckToPostMessage<MetalState, MetalMsg>(eEvent.ERR_METAL, Module.ToString(), (int)MetalMsg.CurrentShortTest); });            OP.Subscribe($"{Module}.UpdateMetalUsage", UpdateMetalUsageAction);            OP.Subscribe($"{Module}.Abort", (cmd, args) => { return CheckToPostMessage<MetalState, MetalMsg>(eEvent.ERR_METAL, Module.ToString(), (int)MetalMsg.Abort); });        }        /// <summary>        /// EnterInit        /// </summary>        public void EnterInit()        {            if ((MetalState)fsm.State != MetalState.Idle) return;            else            {                CheckToPostMessage<MetalState, MetalMsg>(eEvent.ERR_METAL, Module.ToString(), (int)MetalMsg.Init);            }        }        /// 初始化状态机        /// </summary>        private void InitialFsm()        {            fsm = new StateMachine<MetalEntity>(Module.ToString(), (int)MetalState.Init, 20);            fsm.EnableRepeatedMsg(true);            AnyStateTransition(MetalMsg.Error, EnterError, MetalState.Error);            //Initialize            AnyStateTransition(MetalMsg.Initialize, InitializeAll, MetalState.Initializing);            Transition(MetalState.Initializing, FSM_MSG.TIMER, InitializeAllMonitor, MetalState.Initialized);            //直接进入Idle            Transition(MetalState.Initialized, FSM_MSG.TIMER, NullFunc, MetalState.Idle);            //RunRecipe            Transition(MetalState.Idle, MetalMsg.RunRecipe, RunRecipe, MetalState.RunReciping);            Transition(MetalState.WaitForRunRecipe, MetalMsg.RunRecipe, RunRecipe, MetalState.RunReciping);            Transition(MetalState.RunReciping, FSM_MSG.TIMER, RunRecipeMonitor, MetalState.Idle);            Transition(MetalState.Idle, FSM_MSG.TIMER, MetalCompleteSoakTimeMonitor, MetalState.Idle);            Transition(MetalState.RunReciping, MetalMsg.Abort, AbortRecipe, MetalState.Abort);            //Current Short Test            Transition(MetalState.Idle, MetalMsg.CurrentShortTest, CurrentShortTest, MetalState.CurrentShortTesting);            Transition(MetalState.CurrentShortTesting, FSM_MSG.TIMER, CurrentShortTestMonitor, MetalState.WaitCloseFlow);            Transition(MetalState.WaitCloseFlow, MetalMsg.CloseFlowValve, CloseFlowValve, MetalState.WaitOpenFlow);            Transition(MetalState.WaitOpenFlow, MetalMsg.OpenFlowValve, OpenFlowValve, MetalState.WaitForRunRecipe);            //Enter Init            Transition(MetalState.Idle, MetalMsg.Init, NullFunc, MetalState.Init);            EnumLoop<MetalState>.ForEach((item) => { fsm.MapState((int)item, item.ToString()); });            EnumLoop<MetalMsg>.ForEach((item) => { fsm.MapMessage((int)item, item.ToString()); });        }        /// <summary>        /// 初始化DATA        /// </summary>        private void InitializeDATA()        {            InitializeSVID();            DATA.Subscribe($"{Module}.FsmState", () => ((MetalState)fsm.State).ToString(), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.WaferHolder", () => WaferHolderInfo, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AchievedCycle", () => AchievedCycle, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.IsInit", () => IsInit, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.IsIdle", () => IsIdle, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.IsError", () => IsError, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.IsBusy", () => IsBusy, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.IsDisable", () => IsDisable, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.CurrentRecipe", () => _currentRecipe != null ? _currentRecipe.Ppid : "", SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.MetalUsage", () => MetalUsage, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.SubordinateReservoirName", () => _reservoirName, SubscriptionAttribute.FLAG.IgnoreSaveDB);        }        /// <summary>        /// 初始化SVID        /// </summary>        private void InitializeSVID()        {            DATA.Subscribe($"{Module}.State", () => ((MetalState)fsm.State).ToString(), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.LotID", () => WaferHolderInfo?.LotId , SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.WSID", () => WaferHolderInfo?.Id, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.LSAID", () => WaferHolderInfo?.CrsAId, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.LSBID", () => WaferHolderInfo?.CrsBId, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.ModuleRecipe", () => _currentRecipe?.Ppid, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.SequenceRecipe", () =>WaferHolderInfo?.SequenceId, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.WaferAID", () => WaferHolderInfo?.WaferAId, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.WaferBID", () => WaferHolderInfo?.WaferBId, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.TotalTime", () => _recipeTime, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.TimeRemain", () => _recipeTime != 0 && _currentRunRecipeRoutine != null ? (_recipeTime - Math.Round((double)_currentRunRecipeRoutine.ElapsedMilliseconds / 1000, 0)) : 0, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.Task", () => WaferHolderInfo?.CurrentControlJobId, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.OperatingMode", () => _persistentValue != null ? _persistentValue.OperatingMode : "None", SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.TotalUsage", () => MetalUsage != null ? MetalUsage.TotalUsage : 0, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.TotalUsage.WarningLimit", () => SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.TotalUsage.FaultLimit", () => SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursFaultLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AnodeAUsage", () => MetalUsage != null ? MetalUsage.AnodeAUsage : 0, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AnodeAUsage.WarningLimit", () => SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AnodeAUsage.FaultLimit", () => SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AnodeBUsage", () => MetalUsage != null ? MetalUsage.AnodeBUsage : 0, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AnodeBUsage.WarningLimit", () => SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AnodeBUsage.FaultLimit", () => SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.MembranceAUsage", () => MetalUsage != null ? MetalUsage.MembranceAUsage : 0, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.MembranceAUsage.WarningLimit", () => SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.MembranceAUsage.FaultLimit", () => SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.MembranceBUsage", () => MetalUsage != null ? MetalUsage.MembranceBUsage : 0, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.MembranceBUsage.WarningLimit", () => SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.MembranceBUsage.FaultLimit", () => SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.TotalWafers", () => MetalUsage != null ? MetalUsage.TotalWafers : 0, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.TotalWafers.WarningLimit", () => SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.TotalWafers.FaultLimit", () => SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersFaultLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AnodeAWafers", () => MetalUsage != null ? MetalUsage.AnodeAWafers : 0, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AnodeAWafers.WarningLimit", () => SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AnodeAWafers.FaultLimit", () => SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersFaultLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AnodeBWafers", () => MetalUsage != null ? MetalUsage.AnodeBWafers : 0, SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AnodeBWafers.WarningLimit", () => SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);            DATA.Subscribe($"{Module}.AnodeBWafers.FaultLimit", () => SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersFaultLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);        }        /// Metal完成recipe后监控wafershuttle浸泡时间        /// </summary>        /// <param name="param"></param>        /// <returns></returns>        private bool MetalCompleteSoakTimeMonitor(object[] param)        {            if (WaferHolderInfo != null)            {                double sockSeconds = (DateTime.Now - _currentWaferShuttleStartSoakTime).TotalSeconds;                if (_isCurrentReceipeComplete && sockSeconds > _waferShuttleSoakMaxTime * 60)                {                    if (!AlarmListManager.Instance.IsContainDataWarn(Module.ToString(), "MetalSockTime"))                    {                        AlarmListManager.Instance.AddWarn(Module.ToString(),                           $"MetalSockTime", $"Current waferShuttle:{WaferHolderInfo.Id} sock time is over waferShuttleSoakMaxTime:{_waferShuttleSoakMaxTime} minutes");                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"Current waferShuttle:{WaferHolderInfo.Id} sock time is over waferShuttleSoakMaxTime:{_waferShuttleSoakMaxTime} minutes");                    }                }            }            else            {                _isCurrentReceipeComplete = false;  //wafershuttle拿走后重置当前recipe是否完成变量            }            return true;        }        /// <summary>        /// 进入错误状态        /// </summary>        /// <param name="args"></param>        /// <returns></returns>        private bool EnterError(object[] args)        {            if (State == (int)MetalState.RunReciping)            {                if (_metalItem.SubType == STRATUS)                {                    _standardHotRunRecipeRoutine.Abort();                }                else                {                    _compactEmbranceRunRecipeRoutine.Abort();                }            }            return true;        }        /// <summary>        /// UpdateMetalUsageAction        /// </summary>        /// <param name="cmd"></param>        /// <param name="param"></param>        /// <returns></returns>        private bool UpdateMetalUsageAction(string cmd, object[] args)        {            if (args.Length > 2)            {                MetalUsageManager.Instance.UpdateMetalUsageByManualOperation(args[0].ToString(), args[1].ToString(), args[2].ToString());                LOG.WriteLog(eEvent.INFO_METAL, Module.ToString(), $"New {args[1].ToString()} value {args[2].ToString()} was input");            }            return true;        }        #region Initialize All        /// <summary>        /// 初始化        /// </summary>        /// <param name="param"></param>        /// <returns></returns>        private bool InitializeAll(object[] param)        {            if (fsm.State == (int)MetalState.Initializing)            {                LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), "state is Initializing,cannot do initialize");                return false;            }            if (!CheckReservoirInitialized())            {                LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), "Reservoir is not initialized");                return false;            }            if (!MetalUsageMointor(Module.ToString()))            {                return false;            }            if ("Auto".Equals(_persistentValue.OperatingMode))            {                if (!CheckReservoirIsAuto())                {                    LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), "Reservoir is not in Auto OperationMode");                    return false;                }            }            if (_metalItem.SubType == STRATUS)            {                return _standardHotInitializeRoutine.Start(_persistentValue) == RState.Running;            }            else            {                return _compactEmbranceInitializeRoutine.Start(_persistentValue) == RState.Running;            }        }        /// <summary>        /// 检验Reservoir是否Initialized        /// </summary>        /// <returns></returns>        private bool CheckReservoirInitialized()        {            string reservoir = ReservoirItemManager.Instance.GetReservoirByMetal(Module.ToString());            ReservoirEntity reservoirEntity = Singleton<RouteManager>.Instance.GetModule<ReservoirEntity>(reservoir);            if (reservoirEntity != null)            {                return reservoirEntity.IsInitialized;            }            return false;        }        /// <summary>        /// 检验Reservoir是否Auto        /// </summary>        /// <returns></returns>        private bool CheckReservoirIsAuto()        {            string reservoir = ReservoirItemManager.Instance.GetReservoirByMetal(Module.ToString());            ReservoirsPersistentValue reservoirsPersistentValue = ReservoirsPersistentManager.Instance.GetReservoirsPersistentValue(reservoir);            if ("Auto".Equals(reservoirsPersistentValue.OperatingMode))            {                return true;            }            return false;        }        /// <summary>        /// 初始化监控        /// </summary>        /// <param name="param"></param>        /// <returns></returns>        private bool InitializeAllMonitor(object[] param)        {            RState rsstate = RState.Running;            if (_metalItem.SubType == STRATUS)            {                rsstate = _standardHotInitializeRoutine.Monitor();            }            else            {                rsstate = _compactEmbranceInitializeRoutine.Monitor();            }            if (rsstate == RState.End)            {                return true;            }            else if (rsstate == RState.Failed || rsstate == RState.Timeout)            {                PostMsg(MetalMsg.Error);                return false;            }            return false;        }        #endregion        #region RunRecipe        /// <summary>        /// Run Recipe        /// </summary>        /// <param name="param"></param>        /// <returns></returns>        private bool RunRecipe(object[] param)        {            MetalUsageMointor(); //Metal run recipe前检查PMCounter消耗            string reservoirName = ReservoirItemManager.Instance.GetReservoirByMetal(Module.ToString());            if (_metalItem.SubType == STRATUS)            {                StandardHotReservoirDevice resDevice = DEVICE.GetDevice<StandardHotReservoirDevice>(reservoirName);                resDevice.ReservoirUsageMonitor();            }            DepRecipe recipe = param[0] as DepRecipe;            _recipeSide = param[1].ToString();            _cycle = (int)param[2];            _runrecipeElapsedTime = 0;            bool result = false;            if (_metalItem.SubType == STRATUS)            {                _currentRunRecipeRoutine = _standardHotRunRecipeRoutine;                result = _standardHotRunRecipeRoutine.Start(recipe, _recipeSide,_cycle) == RState.Running;            }            else            {                _currentRunRecipeRoutine = _compactEmbranceRunRecipeRoutine;                result = _compactEmbranceRunRecipeRoutine.Start(recipe, _recipeSide, _cycle) == RState.Running;            }            if (result)            {                if (WaferHolderInfo != null)                {                    WaferHolderInfo.MetalModuleName = Module;                }                _recipeTime = (recipe.CalculateRecipeTotalTime())*_cycle;                _currentRecipe = recipe;                if (WaferHolderInfo != null && WaferHolderInfo.SchedulerModules != null && WaferHolderInfo.SchedulerModules.Count != 0)                {                    LOG.WriteLog(eEvent.INFO_METAL, Module.ToString(), $"{WaferHolderInfo?.Id} {string.Join(",", WaferHolderInfo.SchedulerModules)}");                }                _runRecipeStartTime = DateTime.Now;                if (WaferHolderInfo != null && _currentRecipe != null)                {                    FaModuleNotifier.Instance.NotifyWaferShuttleRecipeStart(WaferHolderInfo, _currentRecipe.Ppid);                }            }            return result;        }        /// <summary>        /// RunRecipe监控        /// </summary>        /// <param name="param"></param>        /// <returns></returns>        private bool RunRecipeMonitor(object[] param)        {            RState rsstate = RState.Running;            if (_metalItem.SubType == STRATUS)            {                rsstate = _standardHotRunRecipeRoutine.Monitor();                if (rsstate == RState.Running)                {                    StandardHotMetalDevice _standardHotMetalDevice = DEVICE.GetDevice<StandardHotMetalDevice>(Module.ToString());                    if (_standardHotMetalDevice.MetalDeviceData.CellFlow <= 0)                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"reservoir metal cell flow is 0");                        rsstate = RState.Failed;                    }                }                _achievedCycle = _standardHotRunRecipeRoutine.GetCurrentCycle();            }            else            {                rsstate = _compactEmbranceRunRecipeRoutine.Monitor();                if (rsstate == RState.Running)                {                    CompactMembranMetalDevice _compactMembranMetalDevice = DEVICE.GetDevice<CompactMembranMetalDevice>(Module.ToString());                    if (_compactMembranMetalDevice.MetalDeviceData.CellFlow <= 0)                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"reservoir metal cell flow is 0");                        rsstate = RState.Failed;                    }                    if (_compactMembranMetalDevice.ANACellFlow.CounterValue <= 0) //检查hold flow                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"reservoir AnodeA flow is 0");                        rsstate = RState.Failed;                    }                    if (_compactMembranMetalDevice.ANBCellFlow.CounterValue <= 0)                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"reservoir AnodeB flow is 0");                        rsstate = RState.Failed;                    }                }            }            if (Singleton<RouteManager>.Instance.IsAutoRunning && _runrecipeElapsedTime != TimeToReady)            {                _runrecipeElapsedTime = TimeToReady;                LOG.WriteLog(eEvent.INFO_METAL, Module.ToString(), $"{WaferHolderInfo?.Id} {Module} RunRecipe TimeToReady {TimeToReady}s.");            }            if (rsstate == RState.End)            {                _recipeTime = 0;                if (WaferHolderInfo != null)                {                    double anodeAUsage = GetAnodeAUsage();                    double anodeBUsage = GetAnodeBUsage();                    MetalUsageManager.Instance.UpdateMetalUsage(Module.ToString(), _recipeSide, anodeAUsage, anodeBUsage);                    MetalUsageMointor(); //检查PMCounter消耗                    //CMM Usage                    string reservoirName = ReservoirItemManager.Instance.GetReservoirByMetal(Module.ToString());                    if (_metalItem.SubType == STRATUS)                    {                        StandardHotReservoirDevice resDevice = DEVICE.GetDevice<StandardHotReservoirDevice>(reservoirName);                        resDevice.ReservoirUsageMonitor();                        resDevice.SetExportCMMUsage();                    }                    WaferHolderInfo.LastMetalRecipeCompleteTime = DateTime.Now;                    _isCurrentReceipeComplete = true;                    _currentWaferShuttleStartSoakTime = DateTime.Now;                }                _runrecipeElapsedTime = 0;                //记录LotTrack                _runRecipeCompleteTime = DateTime.Now;                int timeLength = (int)(_runRecipeCompleteTime - _runRecipeStartTime).TotalSeconds;                if (_metalItem.SubType == STRATUS)                {                    _standardHotRunRecipeRoutine.MetalLotTrackHeaderDatas.ProcessTime = (_runRecipeCompleteTime - _runRecipeStartTime).TotalSeconds.ToString("F2");                    MetalLotTrackUtil.ExportMetalLotTrack(Module.ToString(), _standardHotRunRecipeRoutine.MetalLotTrackDatas,                        _standardHotRunRecipeRoutine.MetalLotTrackHeaderDatas, IsAuto, _currentRecipe, _metalItem.SubType);                }                else                {                    _compactEmbranceRunRecipeRoutine.MetalLotTrackHeaderDatas.ProcessTime = (_runRecipeCompleteTime - _runRecipeStartTime).TotalSeconds.ToString("F2");                    MetalLotTrackUtil.ExportMetalLotTrack(Module.ToString(), _compactEmbranceRunRecipeRoutine.MetalLotTrackDatas,                        _compactEmbranceRunRecipeRoutine.MetalLotTrackHeaderDatas, IsAuto, _currentRecipe, _metalItem.SubType);                }                if (WaferHolderInfo != null && _currentRecipe != null)                {                    FaModuleNotifier.Instance.NotifyWaferShuttleRecipeEnd(WaferHolderInfo, _currentRecipe.Ppid, timeLength);                }                return true;            }            else if (rsstate == RState.Failed || rsstate == RState.Timeout)            {                _recipeTime = 0;                if (WaferHolderInfo != null)                {                    double anodeAUsage = GetAnodeAUsage();                    double anodeBUsage = GetAnodeBUsage();                    MetalUsageManager.Instance.UpdateMetalUsage(Module.ToString(), _recipeSide, anodeAUsage, anodeBUsage);                    MetalUsageMointor(); //失败了也要检查PMCounter消耗                    //CMMUsage                    string reservoirName = ReservoirItemManager.Instance.GetReservoirByMetal(Module.ToString());                    if (_metalItem.SubType == STRATUS)                    {                        StandardHotReservoirDevice resDevice = DEVICE.GetDevice<StandardHotReservoirDevice>(reservoirName);                        resDevice.ReservoirUsageMonitor();                        resDevice.SetExportCMMUsage();                    }                }                _runrecipeElapsedTime = 0;                if (WaferHolderInfo != null && _currentRecipe != null)                {                    WaferHolderInfo.LastMetalRecipeCompleteTime = DateTime.Now;                }                PostMsg(MetalMsg.Error);                //记录LotTrack                _runRecipeCompleteTime = DateTime.Now;                if (_metalItem.SubType == STRATUS)                {                    _standardHotRunRecipeRoutine.MetalLotTrackHeaderDatas.ProcessTime = (_runRecipeCompleteTime - _runRecipeStartTime).TotalSeconds.ToString("F2");                    MetalLotTrackUtil.ExportMetalLotTrack(Module.ToString(), _standardHotRunRecipeRoutine.MetalLotTrackDatas,                        _standardHotRunRecipeRoutine.MetalLotTrackHeaderDatas, IsAuto, _currentRecipe, _metalItem.SubType);                }                else                {                    _compactEmbranceRunRecipeRoutine.MetalLotTrackHeaderDatas.ProcessTime = (_runRecipeCompleteTime - _runRecipeStartTime).TotalSeconds.ToString("F2");                    MetalLotTrackUtil.ExportMetalLotTrack(Module.ToString(), _compactEmbranceRunRecipeRoutine.MetalLotTrackDatas,                        _compactEmbranceRunRecipeRoutine.MetalLotTrackHeaderDatas, IsAuto, _currentRecipe, _metalItem.SubType);                }                if (WaferHolderInfo != null && _currentRecipe != null)                {                    FaModuleNotifier.Instance.NotifyWaferShuttleRecipeFailed(WaferHolderInfo, _currentRecipe.Ppid);                }                return false;            }            return false;        }        /// <summary>        /// 监控 PM Counter Metal用量        /// </summary>        private void MetalUsageMointor()        {            MetalUsage metalUsage = MetalUsageManager.Instance.GetMetalUsage(Module.ToString());            if (metalUsage != null)            {                //TotalAUsage                if (metalUsage.TotalUsage > SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursWarningLimit") && SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursWarningLimit") != 0 && SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursFaultLimit") != 0)                {                    if (metalUsage.TotalUsage > SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.TotalUsage} is exceed MetalTotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursFaultLimit")}");                        PostMsg(MetalMsg.Error);                        AlarmListManager.Instance.AddDataError(Module.ToString(),                            $"{Module.ToString()}.metalUsage.TotalUsage",                            $"{Module.ToString()} usage:{metalUsage.TotalUsage} is exceed MetalTotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursFaultLimit")}");                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.TotalUsage} is exceed MetalTotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursWarningLimit")}");                        AlarmListManager.Instance.AddWarn(Module.ToString(),                            $"{Module.ToString()}.metalUsage.TotalUsage",                            $"{Module.ToString()} usage:{metalUsage.TotalUsage} is exceed MetalTotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursWarningLimit")}");                    }                }                //AnodeAUsage                if (metalUsage.AnodeAUsage > SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit") && SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit") != 0 && SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit") != 0)                {                    if (metalUsage.AnodeAUsage > SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeAUsage} is exceed AnodeATotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit")}");                        PostMsg(MetalMsg.Error);                        AlarmListManager.Instance.AddDataError(Module.ToString(),                            $"{Module.ToString()}.metalUsage.AnodeAUsage",                            $"{Module.ToString()} usage:{metalUsage.AnodeAUsage} is exceed AnodeATotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit")}");                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeAUsage} is exceed AnodeATotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit")}");                        AlarmListManager.Instance.AddWarn(Module.ToString(),                            $"{Module.ToString()}.metalUsage.AnodeAUsage",                            $"{Module.ToString()} usage:{metalUsage.AnodeAUsage} is exceed AnodeATotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit")}");                    }                }                //AnodeBUsage                if (metalUsage.AnodeBUsage > SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit") && SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit") != 0 && SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit") != 0)                {                    if (metalUsage.AnodeBUsage > SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeBUsage} is exceed AnodeBTotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit")}");                        PostMsg(MetalMsg.Error);                        AlarmListManager.Instance.AddDataError(Module.ToString(),                            $"{Module.ToString()}.metalUsage.AnodeBUsage",                            $"{Module.ToString()} usage:{metalUsage.AnodeBUsage} is exceed AnodeBTotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit")}");                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeBUsage} is exceed AnodeBTotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit")}");                        AlarmListManager.Instance.AddWarn(Module.ToString(),                            $"{Module.ToString()}.metalUsage.AnodeBUsage",                            $"{Module.ToString()} usage:{metalUsage.AnodeBUsage} is exceed AnodeBTotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit")}");                    }                }                //MembraneAUsage                if (metalUsage.MembranceAUsage > SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit") && SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit") != 0 && SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit") != 0)                {                    if (metalUsage.MembranceAUsage > SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.MembranceAUsage} is exceed MembraneATotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit")}");                        PostMsg(MetalMsg.Error);                        AlarmListManager.Instance.AddDataError(Module.ToString(),                            $"{Module.ToString()}.metalUsage.MembraneAUsage",                            $"{Module.ToString()} usage:{metalUsage.MembranceAUsage} is exceed MembraneATotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit")}");                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.MembranceAUsage} is exceed MembraneATotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit")}");                        AlarmListManager.Instance.AddWarn(Module.ToString(),                            $"{Module.ToString()}.metalUsage.MembraneAUsage",                            $"{Module.ToString()} usage:{metalUsage.MembranceAUsage} is exceed MembraneATotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit")}");                    }                }                //MembraneBUsage                if (metalUsage.MembranceBUsage > SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit") && SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit") != 0 && SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit") != 0)                {                    if (metalUsage.MembranceBUsage > SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.MembranceBUsage} is exceed MembraneBTotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit")}");                        PostMsg(MetalMsg.Error);                        AlarmListManager.Instance.AddDataError(Module.ToString(),                            $"{Module.ToString()}.metalUsage.MembraneBUsage",                            $"{Module.ToString()} usage:{metalUsage.MembranceBUsage} is exceed MembraneBTotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit")}");                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.MembranceBUsage} is exceed MembraneBTotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit")}");                        AlarmListManager.Instance.AddWarn(Module.ToString(),                            $"{Module.ToString()}.metalUsage.MembraneBUsage",                            $"{Module.ToString()} usage:{metalUsage.MembranceBUsage} is exceed MembraneBTotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit")}");                    }                }                //TotalWafer                if (metalUsage.TotalWafers > SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersWarningLimit") && SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersWarningLimit") != 0 && SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersFaultLimit") != 0)                {                    if (metalUsage.TotalWafers > SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.TotalWafers} is exceed MetalTotalWafersFaultLimit:{SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersFaultLimit")}");                        PostMsg(MetalMsg.Error);                        AlarmListManager.Instance.AddDataError(Module.ToString(),                            $"{Module.ToString()}.metalUsage.TotalWafers",                            $"{Module.ToString()} usage:{metalUsage.TotalWafers} is exceed MetalTotalWafersFaultLimit:{SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersFaultLimit")}");                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.TotalWafers} is exceed MetalTotalWafersWarningLimit:{SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersWarningLimit")}");                        AlarmListManager.Instance.AddWarn(Module.ToString(),                            $"{Module.ToString()}.metalUsage.TotalWafers",                            $"{Module.ToString()} usage:{metalUsage.TotalWafers} is exceed MetalTotalWafersWarningLimit:{SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersWarningLimit")}");                    }                }                //AnodeAWafer                if (metalUsage.AnodeAWafers > SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersWarningLimit") && SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersWarningLimit") != 0 && SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersFaultLimit") != 0)                {                    if (metalUsage.AnodeAWafers > SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeAWafers} is exceed AnodeATotalWafersFaultLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersFaultLimit")}");                        PostMsg(MetalMsg.Error);                        AlarmListManager.Instance.AddDataError(Module.ToString(),                            $"{Module.ToString()}.metalUsage.AnodeAWafers",                            $"{Module.ToString()} usage:{metalUsage.AnodeAWafers} is exceed AnodeATotalWafersFaultLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersFaultLimit")}");                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeAWafers} is exceed AnodeATotalWafersWarningLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersWarningLimit")}");                        AlarmListManager.Instance.AddWarn(Module.ToString(),                            $"{Module.ToString()}.metalUsage.AnodeAWafers",                            $"{Module.ToString()} usage:{metalUsage.AnodeAWafers} is exceed AnodeATotalWafersWarningLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersWarningLimit")}");                    }                }                //AnodeBWafer                if (metalUsage.AnodeBWafers > SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersWarningLimit") && SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersWarningLimit") != 0 && SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersFaultLimit") != 0)                {                    if (metalUsage.AnodeBWafers > SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeBWafers} is exceed AnodeBTotalWafersFaultLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersFaultLimit")}");                        PostMsg(MetalMsg.Error);                        AlarmListManager.Instance.AddDataError(Module.ToString(),                            $"{Module.ToString()}.metalUsage.AnodeBWafers",                            $"{Module.ToString()} usage:{metalUsage.AnodeBWafers} is exceed AnodeBTotalWafersFaultLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersFaultLimit")}");                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeBWafers} is exceed AnodeBTotalWafersWarningLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersWarningLimit")}");                        AlarmListManager.Instance.AddWarn(Module.ToString(),                            $"{Module.ToString()}.metalUsage.AnodeBWafers",                            $"{Module.ToString()} usage:{metalUsage.AnodeBWafers} is exceed AnodeBTotalWafersWarningLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersWarningLimit")}");                    }                }                //AnodeAbathUsage                if (metalUsage.AnodeABathUsage > SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit") && SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit") != 0 && SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit") != 0)                {                    if (metalUsage.AnodeABathUsage > SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeABathUsage} is exceed AnodeABathTotalUsageDaysFaultLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit")}");                        PostMsg(MetalMsg.Error);                        AlarmListManager.Instance.AddDataError(Module.ToString(),                            $"{Module.ToString()}.metalUsage.AnodeABathUsage",                            $"{Module.ToString()} usage:{metalUsage.AnodeABathUsage} is exceed AnodeABathTotalUsageDaysFaultLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit")}");                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeABathUsage} is exceed AnodeABathTotalUsageDaysWarningLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit")}");                        AlarmListManager.Instance.AddWarn(Module.ToString(),                            $"{Module.ToString()}.metalUsage.AnodeABathUsage",                            $"{Module.ToString()} usage:{metalUsage.AnodeABathUsage} is exceed AnodeABathTotalUsageDaysWarningLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit")}");                    }                }                //AnodeBbathUsage                if (metalUsage.AnodeBBathUsage > SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit") && SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit") != 0 && SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit") != 0)                {                    if (metalUsage.AnodeBBathUsage > SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeBBathUsage} is exceed AnodeBBathTotalUsageDaysFaultLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit")}");                        PostMsg(MetalMsg.Error);                        AlarmListManager.Instance.AddDataError(Module.ToString(),                            $"{Module.ToString()}.metalUsage.AnodeBBathUsage",                            $"{Module.ToString()} usage:{metalUsage.AnodeBBathUsage} is exceed AnodeBBathTotalUsageDaysFaultLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit")}");                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeBBathUsage} is exceed AnodeBBathTotalUsageDaysWarningLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit")}");                        AlarmListManager.Instance.AddWarn(Module.ToString(),                            $"{Module.ToString()}.metalUsage.AnodeBBathUsage",                            $"{Module.ToString()} usage:{metalUsage.AnodeBBathUsage} is exceed AnodeBBathTotalUsageDaysWarningLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit")}");                    }                }            }        }        /// <summary>        /// 获取A面使用的电量        /// </summary>        /// <returns></returns>        private double GetAnodeAUsage()        {            if (_metalItem.SubType == STRATUS)            {                return Math.Round(_standardHotRunRecipeRoutine.AnodeAUsage, 3);            }            else            {                return Math.Round(_compactEmbranceRunRecipeRoutine.AnodeAUsage, 3);            }        }        /// <summary>        /// 获取B面使用的电量        /// </summary>        /// <returns></returns>        private double GetAnodeBUsage()        {            if (_metalItem.SubType == STRATUS)            {                return Math.Round(_standardHotRunRecipeRoutine.AnodeBUsage, 3);            }            else            {                return Math.Round(_compactEmbranceRunRecipeRoutine.AnodeBUsage, 3);            }        }        /// <summary>        /// Abort Recipe        /// </summary>        /// <param name="param"></param>        /// <returns></returns>        public bool AbortRecipe(object[] param)        {            _runRecipeCompleteTime = DateTime.Now;            if (_metalItem.SubType == STRATUS)            {                _standardHotRunRecipeRoutine.Abort();                _standardHotRunRecipeRoutine.MetalLotTrackHeaderDatas.ProcessTime = (_runRecipeCompleteTime - _runRecipeStartTime).TotalSeconds.ToString("F2");                MetalLotTrackUtil.ExportMetalLotTrack(Module.ToString(), _standardHotRunRecipeRoutine.MetalLotTrackDatas,                    _standardHotRunRecipeRoutine.MetalLotTrackHeaderDatas, IsAuto, _currentRecipe, _metalItem.SubType);            }            else            {                _compactEmbranceRunRecipeRoutine.Abort();                _compactEmbranceRunRecipeRoutine.MetalLotTrackHeaderDatas.ProcessTime = (_runRecipeCompleteTime - _runRecipeStartTime).TotalSeconds.ToString("F2");                MetalLotTrackUtil.ExportMetalLotTrack(Module.ToString(), _compactEmbranceRunRecipeRoutine.MetalLotTrackDatas,                    _compactEmbranceRunRecipeRoutine.MetalLotTrackHeaderDatas, IsAuto, _currentRecipe, _metalItem.SubType);            }            return true;        }        #endregion        #region Current Short Test        /// <summary>        /// Current Short Test        /// </summary>        /// <param name="param"></param>        /// <returns></returns>        private bool CurrentShortTest(object[] param)        {            return _currentShortTestRoutine.Start() == RState.Running;        }        /// <summary>        /// Current Short test监控        /// </summary>        /// <param name="param"></param>        /// <returns></returns>        private bool CurrentShortTestMonitor(object[] param)        {            RState rsstate = _currentShortTestRoutine.Monitor();            if (rsstate == RState.End)            {                return true;            }            else if (rsstate == RState.Failed || rsstate == RState.Timeout)            {                PostMsg(MetalMsg.Error);                return false;            }            return false;        }        #endregion        #region Close Flow Valve        /// <summary>        /// 关闭Flow Valve        /// </summary>        /// <param name="param"></param>        /// <returns></returns>        private bool CloseFlowValve(object[] param)        {            if (_metalItem.SubType == STRATUS)            {                StandardHotMetalDevice device = DEVICE.GetDevice<StandardHotMetalDevice>(Module.ToString());                if (device != null)                {                    return device.SwitchToBypass("", null);                }                return false;            }            else            {                CompactMembranMetalDevice device = DEVICE.GetDevice<CompactMembranMetalDevice>(Module.ToString());                if (device != null)                {                    string reservoir = ReservoirItemManager.Instance.GetReservoirByMetal(Module.ToString());                    CompactMembranReservoirDevice reservoirDevice = DEVICE.GetDevice<CompactMembranReservoirDevice>(reservoir);                    if (reservoirDevice != null)                    {                        bool result = reservoirDevice.CAByPassOn("", null);                        if (result)                        {                            return device.CellFlowValveOff("", null);                        }                    }                }                return false;            }        }        #endregion        #region Open Flow Valve        /// <summary>        /// 打开Flow Valve        /// </summary>        /// <param name="param"></param>        /// <returns></returns>        private bool OpenFlowValve(object[] param)        {            if (_metalItem.SubType == STRATUS)            {                StandardHotMetalDevice device = DEVICE.GetDevice<StandardHotMetalDevice>(Module.ToString());                if (device != null)                {                    bool result = device.SwitchToFlow("", null);                    if (result)                    {                        return device.OpenPump("", null);                    }                }                return false;            }            else            {                CompactMembranMetalDevice device = DEVICE.GetDevice<CompactMembranMetalDevice>(Module.ToString());                if (device != null)                {                    string reservoir = ReservoirItemManager.Instance.GetReservoirByMetal(Module.ToString());                    CompactMembranReservoirDevice reservoirDevice = DEVICE.GetDevice<CompactMembranReservoirDevice>(reservoir);                    if (reservoirDevice != null)                    {                        bool result = reservoirDevice.CAByPassOff("", null);                        if (result)                        {                            return device.CellFlowValveOn("", null);                        }                    }                }                return false;            }        }        #endregion        public bool Check(int msg, out string reason, params object[] args)        {            reason = "";            return true;        }        public bool CheckAcked(int msg)        {            return true;        }        public int Invoke(string function, params object[] args)        {            switch (function)            {                case "HomeAll":                    if (IsIdle)                    {                        return (int)FSM_MSG.NONE;                    }                    if (CheckToPostMessage<MetalState, MetalMsg>(eEvent.ERR_METAL, Module.ToString(), (int)MetalMsg.Initialize))                    {                        return (int)MetalMsg.Initialize;                    }                    else                    {                        return (int)FSM_MSG.NONE;                    }            }            return (int)FSM_MSG.NONE;        }        /// <summary>        /// 监控 PM Counter Metal用量        /// </summary>        private bool MetalUsageMointor(string Module)        {            MetalUsage metalUsage = MetalUsageManager.Instance.GetMetalUsage(Module);            if (metalUsage != null)            {                //TotalAUsage                if (metalUsage.TotalUsage > SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursWarningLimit") && SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursWarningLimit") != 0 && SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursFaultLimit") != 0)                {                    if (metalUsage.TotalUsage > SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module} usage:{metalUsage.TotalUsage} is exceed MetalTotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursFaultLimit")}");                        return false;                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module} usage:{metalUsage.TotalUsage} is exceed MetalTotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.MetalTotalAmpHoursWarningLimit")}");                    }                }                //AnodeAUsage                if (metalUsage.AnodeAUsage > SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit") && SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit") != 0 && SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit") != 0)                {                    if (metalUsage.AnodeAUsage > SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module} usage:{metalUsage.AnodeAUsage} is exceed AnodeATotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit")}");                        return false;                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.AnodeAUsage} is exceed AnodeATotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit")}");                    }                }                //AnodeBUsage                if (metalUsage.AnodeBUsage > SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit") && SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit") != 0 && SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit") != 0)                {                    if (metalUsage.AnodeBUsage > SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.AnodeBUsage} is exceed AnodeBTotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit")}");                        return false;                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.AnodeBUsage} is exceed AnodeBTotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit")}");                    }                }                //MembraneAUsage                if (metalUsage.MembranceAUsage > SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit") && SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit") != 0 && SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit") != 0)                {                    if (metalUsage.MembranceAUsage > SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.MembranceAUsage} is exceed MembraneATotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit")}");                        return false;                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.MembranceAUsage} is exceed MembraneATotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit")}");                    }                }                //MembraneBUsage                if (metalUsage.MembranceBUsage > SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit") && SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit") != 0 && SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit") != 0)                {                    if (metalUsage.MembranceBUsage > SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.MembranceBUsage} is exceed MembraneBTotalAmpHoursFaultLimit:{SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit")}");                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.MembranceBUsage} is exceed MembraneBTotalAmpHoursWarningLimit:{SC.GetValue<double>($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit")}");                    }                }                //TotalWafer                if (metalUsage.TotalWafers > SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersWarningLimit") && SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersWarningLimit") != 0 && SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersFaultLimit") != 0)                {                    if (metalUsage.TotalWafers > SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.TotalWafers} is exceed MetalTotalWafersFaultLimit:{SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersFaultLimit")}");                        return false;                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.TotalWafers} is exceed MetalTotalWafersWarningLimit:{SC.GetValue<int>($"Metal.{Module}.MetalTotalWafersWarningLimit")}");                    }                }                //AnodeAWafer                if (metalUsage.AnodeAWafers > SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersWarningLimit") && SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersWarningLimit") != 0 && SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersFaultLimit") != 0)                {                    if (metalUsage.AnodeAWafers > SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.AnodeAWafers} is exceed AnodeATotalWafersFaultLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersFaultLimit")}");                        return false;                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.AnodeAWafers} is exceed AnodeATotalWafersWarningLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeATotalWafersWarningLimit")}");                    }                }                //AnodeBWafer                if (metalUsage.AnodeBWafers > SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersWarningLimit") && SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersWarningLimit") != 0 && SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersFaultLimit") != 0)                {                    if (metalUsage.AnodeBWafers > SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.AnodeBWafers} is exceed AnodeBTotalWafersFaultLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersFaultLimit")}");                        return false;                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.AnodeBWafers} is exceed AnodeBTotalWafersWarningLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeBTotalWafersWarningLimit")}");                    }                }                //AnodeAbathUsage                if (metalUsage.AnodeABathUsage > SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit") && SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit") != 0 && SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit") != 0)                {                    if (metalUsage.AnodeABathUsage > SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.AnodeABathUsage} is exceed AnodeABathTotalUsageDaysFaultLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit")}");                        return false;                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.AnodeABathUsage} is exceed AnodeABathTotalUsageDaysWarningLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit")}");                    }                }                //AnodeBbathUsage                if (metalUsage.AnodeBBathUsage > SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit") && SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit") != 0 && SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit") != 0)                {                    if (metalUsage.AnodeBBathUsage > SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit"))                    {                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.AnodeBBathUsage} is exceed AnodeBBathTotalUsageDaysFaultLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit")}");                        return false;                    }                    else                    {                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.AnodeBBathUsage} is exceed AnodeBBathTotalUsageDaysWarningLimit:{SC.GetValue<int>($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit")}");                    }                }            }            return true;        }    }    public enum MetalMsg    {        NONE,        Error,        ResumeError,        Initialize,        Manual,        Auto,        CurrentShortTest,        CloseFlowValve,        OpenFlowValve,        RunRecipe,        Abort,        Init    }}
 |