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 内部变量
        /// 
        /// 已经完成的Cycle次数
        /// 
        private int _achievedCycle = 0;
        /// 
        /// Metal项
        /// 
        private MetalItem _metalItem;
        /// 
        /// 持久化对象
        /// 
        private MetalPersistentValue _persistentValue;
        /// 
        /// c&m Initialize routine
        /// 
        private CompactEmbranceInitializeRoutine _compactEmbranceInitializeRoutine;
        /// 
        /// C&M Runrecipe routine
        /// 
        private CompactEmbranceRunRecipeRoutine _compactEmbranceRunRecipeRoutine;
        /// 
        /// S&H Initialize Routine
        /// 
        private StandardHotInitializeRoutine _standardHotInitializeRoutine;
        /// 
        /// S&H RunRecipe Routine
        /// 
        private StandardHotRunRecipeRoutine _standardHotRunRecipeRoutine;
        /// 
        /// Current short test routine
        /// 
        private CurrentShortTestRoutine _currentShortTestRoutine;
        /// 
        /// 当前Recipe
        /// 
        private DepRecipe _currentRecipe = null;
        /// 
        /// recipe时间
        /// 
        private int _recipeTime;
        /// 
        /// 循环次数
        /// 
        private int _cycle = 0;
        /// 
        /// 运行Recipe面
        /// 
        private string _recipeSide = "";
        /// 
        /// 当前RunRecipe Routine
        /// 
        private RoutineBase _currentRunRecipeRoutine;
        /// 
        /// Runrecipe已经运行的时间
        /// 
        private int _runrecipeElapsedTime = 0;
        /// 
        /// run recipe start time
        /// 
        private DateTime _runRecipeStartTime;
        /// 
        ///  run recipe complete time
        /// 
        private DateTime _runRecipeCompleteTime;
        /// 
        /// 所在reservoir
        /// 
        private StandardHotReservoirDevice _reservoirDevice;
        /// 
        /// 所在reservoir名字
        /// 
        private string _reservoirName;
        /// 
        /// 当前完成recipe次数
        /// 
        private int _currentCycleTimes;
        /// 
        /// 当前recipe是否完成
        /// 
        private bool _isCurrentReceipeComplete;
        /// 
        ///  当前wafershuttle开始浸泡时间
        /// 
        private DateTime _currentWaferShuttleStartSoakTime;
        /// 
        /// Wafershuttle最大浸泡时间
        /// 
        private int _waferShuttleSoakMaxTime;
        #endregion
        #region 属性
        /// 
        /// 模块名称
        /// 
        public ModuleName Module { get; private set; }
        /// 
        /// 初始化状态
        /// 
        public bool IsInit { get { return fsm.State == (int)MetalState.Init; } }
        /// 
        /// 空闲状态
        /// 
        public bool IsIdle { get { return fsm.State == (int)MetalState.Idle; } }
        /// 
        /// 是否发生错误
        /// 
        public bool IsError { get { return fsm.State == (int)MetalState.Error; } }
        /// 
        /// 是否正在作业
        /// 
        public bool IsBusy { get { return fsm.State > (int)MetalState.Idle; } }
        /// 
        /// 是否初始化完成
        /// 
        public bool IsInitialized { get { return fsm.State >= (int)MetalState.Initialized; } }
        /// 
        /// 是否禁用
        /// 
        public bool IsDisable { get { return _persistentValue == null || _persistentValue.OperatingMode == DISABLED; } }
        /// 
        /// 自动模式
        /// 
        public bool IsAuto { get { return _persistentValue != null && _persistentValue.OperatingMode == AUTO; } }
        /// 
        /// 自动模式
        /// 
        public bool IsManual { get { return _persistentValue != null && _persistentValue.OperatingMode == MANUAL; } }
        /// 
        /// 是否为工程模式
        /// 
        public bool IsEngineering { get { return _persistentValue != null && _persistentValue.RecipeOperatingMode == ENGINEERING; } }
        /// 
        /// 是否为产品模式
        /// 
        public bool IsProduction { get { return _persistentValue != null && _persistentValue.RecipeOperatingMode == PRODUCTION; } }
        /// 
        /// WaferHolder信息
        /// 
        public WaferHolderInfo WaferHolderInfo { get { return WaferHolderManager.Instance.GetWaferHolder(Module.ToString()); } }
        /// 
        /// 已完成的RunRecipeCycle次数
        /// 
        public int AchievedCycle { get { return _achievedCycle; } }
        /// 
        /// 当前状态机状态
        /// 
        public int State { get { return fsm.State; } }
        /// 
        /// 当前Metal设置的WaferSize
        /// 
        public int MetalWaferSize { get { return _persistentValue.MetalWaferSize; } }
        /// 
        /// recipe时长
        /// 
        public int RecipeTime { get { return _recipeTime; } }
        /// 
        /// 剩余时间
        /// 
        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;
                }
            }
        }
        /// 
        /// 用量
        /// 
        public MetalUsage MetalUsage { get { return MetalUsageManager.Instance.GetMetalUsage(Module.ToString()); } }
        #endregion
        /// 
        /// 构造函数
        /// 
        /// 
        public MetalEntity(ModuleName moduleName)
        {
            this.Module = moduleName;
            WaferManager.Instance.SubscribeLocation(Module, 2);
            InitialFsm();
        }
        /// 
        /// 初始化
        /// 
        /// 
        protected override bool Init()
        {
            InitializeDATA();
            InitializeParameter();
            InitializeRoutine();
            InitializeOperation();
            return true;
        }
        /// 
        /// 初始化参数
        /// 
        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($"{_reservoirName}");
            _isCurrentReceipeComplete = false;
            _waferShuttleSoakMaxTime = SC.GetValue($"Metal.WaferShuttleSoakMaxTime");
        }
        /// 
        /// 初始化Routine
        /// 
        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());
        }
        /// 
        /// 初始化操作
        /// 
        private void InitializeOperation()
        {
            OP.Subscribe($"{Module}.InitializeAll", (cmd, args) => { return CheckToPostMessage(eEvent.ERR_METAL, Module.ToString(), (int)MetalMsg.Initialize); });
            OP.Subscribe($"{Module}.CycleManualProcessRecipe", (cmd, args) =>
            {
                DepRecipe recipe = RecipeFileManager.Instance.LoadGenericityRecipe(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(eEvent.ERR_METAL, Module.ToString(), (int)MetalMsg.RunRecipe, objects);
            });
            OP.Subscribe($"{Module}.CurrentShortTest", (cmd, args) => { return CheckToPostMessage(eEvent.ERR_METAL, Module.ToString(), (int)MetalMsg.CurrentShortTest); });
            OP.Subscribe($"{Module}.UpdateMetalUsage", UpdateMetalUsageAction);
            OP.Subscribe($"{Module}.Abort", (cmd, args) => { return CheckToPostMessage(eEvent.ERR_METAL, Module.ToString(), (int)MetalMsg.Abort); });
        }
        /// 
        /// EnterInit
        /// 
        public void EnterInit()
        {
            if ((MetalState)fsm.State != MetalState.Idle) return;
            else
            {
                CheckToPostMessage(eEvent.ERR_METAL, Module.ToString(), (int)MetalMsg.Init);
            }
        }
        /// 初始化状态机
        /// 
        private void InitialFsm()
        {
            fsm = new StateMachine(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.ForEach((item) => { fsm.MapState((int)item, item.ToString()); });
            EnumLoop.ForEach((item) => { fsm.MapMessage((int)item, item.ToString()); });
        }
        /// 
        /// 初始化DATA
        /// 
        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);
        }
        /// 
        /// 初始化SVID
        /// 
        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($"Metal.{Module}.MetalTotalAmpHoursWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.TotalUsage.FaultLimit", () => SC.GetValue($"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($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.AnodeAUsage.FaultLimit", () => SC.GetValue($"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($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.AnodeBUsage.FaultLimit", () => SC.GetValue($"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($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.MembranceAUsage.FaultLimit", () => SC.GetValue($"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($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.MembranceBUsage.FaultLimit", () => SC.GetValue($"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($"Metal.{Module}.MetalTotalWafersWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.TotalWafers.FaultLimit", () => SC.GetValue($"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($"Metal.{Module}.AnodeATotalWafersWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.AnodeAWafers.FaultLimit", () => SC.GetValue($"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($"Metal.{Module}.AnodeBTotalWafersWarningLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.AnodeBWafers.FaultLimit", () => SC.GetValue($"Metal.{Module}.AnodeBTotalWafersFaultLimit"), SubscriptionAttribute.FLAG.IgnoreSaveDB);
        }
        /// Metal完成recipe后监控wafershuttle浸泡时间
        /// 
        /// 
        /// 
        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;
        }
        /// 
        /// 进入错误状态
        /// 
        /// 
        /// 
        private bool EnterError(object[] args)
        {
            if (State == (int)MetalState.RunReciping)
            {
                if (_metalItem.SubType == STRATUS)
                {
                    _standardHotRunRecipeRoutine.Abort();
                }
                else
                {
                    _compactEmbranceRunRecipeRoutine.Abort();
                }
            }
            return true;
        }
        /// 
        /// UpdateMetalUsageAction
        /// 
        /// 
        /// 
        /// 
        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
        /// 
        /// 初始化
        /// 
        /// 
        /// 
        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;
            }
        }
        /// 
        /// 检验Reservoir是否Initialized
        /// 
        /// 
        private bool CheckReservoirInitialized()
        {
            string reservoir = ReservoirItemManager.Instance.GetReservoirByMetal(Module.ToString());
            ReservoirEntity reservoirEntity = Singleton.Instance.GetModule(reservoir);
            if (reservoirEntity != null)
            {
                return reservoirEntity.IsInitialized;
            }
            return false;
        }
        /// 
        /// 检验Reservoir是否Auto
        /// 
        /// 
        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;
        }
        /// 
        /// 初始化监控
        /// 
        /// 
        /// 
        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
        /// 
        /// Run Recipe
        /// 
        /// 
        /// 
        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(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;
        }
        /// 
        /// RunRecipe监控
        /// 
        /// 
        /// 
        private bool RunRecipeMonitor(object[] param)
        {
            RState rsstate = RState.Running;
            if (_metalItem.SubType == STRATUS)
            {
                rsstate = _standardHotRunRecipeRoutine.Monitor();
                if (rsstate == RState.Running)
                {
                    StandardHotMetalDevice _standardHotMetalDevice = DEVICE.GetDevice(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(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.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(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(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;
        }
        /// 
        /// 监控 PM Counter Metal用量
        /// 
        private void MetalUsageMointor()
        {
            MetalUsage metalUsage = MetalUsageManager.Instance.GetMetalUsage(Module.ToString());
            if (metalUsage != null)
            {
                //TotalAUsage
                if (metalUsage.TotalUsage > SC.GetValue($"Metal.{Module}.MetalTotalAmpHoursWarningLimit") && SC.GetValue($"Metal.{Module}.MetalTotalAmpHoursWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.MetalTotalAmpHoursFaultLimit") != 0)
                {
                    if (metalUsage.TotalUsage > SC.GetValue($"Metal.{Module}.MetalTotalAmpHoursFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.TotalUsage} is exceed MetalTotalAmpHoursFaultLimit:{SC.GetValue($"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($"Metal.{Module}.MetalTotalAmpHoursFaultLimit")}");
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.TotalUsage} is exceed MetalTotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.MetalTotalAmpHoursWarningLimit")}");
                        AlarmListManager.Instance.AddWarn(Module.ToString(),
                            $"{Module.ToString()}.metalUsage.TotalUsage",
                            $"{Module.ToString()} usage:{metalUsage.TotalUsage} is exceed MetalTotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.MetalTotalAmpHoursWarningLimit")}");
                    }
                }
                //AnodeAUsage
                if (metalUsage.AnodeAUsage > SC.GetValue($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit") && SC.GetValue($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit") != 0)
                {
                    if (metalUsage.AnodeAUsage > SC.GetValue($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeAUsage} is exceed AnodeATotalAmpHoursFaultLimit:{SC.GetValue($"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($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit")}");
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeAUsage} is exceed AnodeATotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit")}");
                        AlarmListManager.Instance.AddWarn(Module.ToString(),
                            $"{Module.ToString()}.metalUsage.AnodeAUsage",
                            $"{Module.ToString()} usage:{metalUsage.AnodeAUsage} is exceed AnodeATotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit")}");
                    }
                }
                //AnodeBUsage
                if (metalUsage.AnodeBUsage > SC.GetValue($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit") && SC.GetValue($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit") != 0)
                {
                    if (metalUsage.AnodeBUsage > SC.GetValue($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeBUsage} is exceed AnodeBTotalAmpHoursFaultLimit:{SC.GetValue($"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($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit")}");
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeBUsage} is exceed AnodeBTotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit")}");
                        AlarmListManager.Instance.AddWarn(Module.ToString(),
                            $"{Module.ToString()}.metalUsage.AnodeBUsage",
                            $"{Module.ToString()} usage:{metalUsage.AnodeBUsage} is exceed AnodeBTotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit")}");
                    }
                }
                //MembraneAUsage
                if (metalUsage.MembranceAUsage > SC.GetValue($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit") && SC.GetValue($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit") != 0)
                {
                    if (metalUsage.MembranceAUsage > SC.GetValue($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.MembranceAUsage} is exceed MembraneATotalAmpHoursFaultLimit:{SC.GetValue($"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($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit")}");
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.MembranceAUsage} is exceed MembraneATotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit")}");
                        AlarmListManager.Instance.AddWarn(Module.ToString(),
                            $"{Module.ToString()}.metalUsage.MembraneAUsage",
                            $"{Module.ToString()} usage:{metalUsage.MembranceAUsage} is exceed MembraneATotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit")}");
                    }
                }
                //MembraneBUsage
                if (metalUsage.MembranceBUsage > SC.GetValue($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit") && SC.GetValue($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit") != 0)
                {
                    if (metalUsage.MembranceBUsage > SC.GetValue($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.MembranceBUsage} is exceed MembraneBTotalAmpHoursFaultLimit:{SC.GetValue($"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($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit")}");
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.MembranceBUsage} is exceed MembraneBTotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit")}");
                        AlarmListManager.Instance.AddWarn(Module.ToString(),
                            $"{Module.ToString()}.metalUsage.MembraneBUsage",
                            $"{Module.ToString()} usage:{metalUsage.MembranceBUsage} is exceed MembraneBTotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit")}");
                    }
                }
                //TotalWafer
                if (metalUsage.TotalWafers > SC.GetValue($"Metal.{Module}.MetalTotalWafersWarningLimit") && SC.GetValue($"Metal.{Module}.MetalTotalWafersWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.MetalTotalWafersFaultLimit") != 0)
                {
                    if (metalUsage.TotalWafers > SC.GetValue($"Metal.{Module}.MetalTotalWafersFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.TotalWafers} is exceed MetalTotalWafersFaultLimit:{SC.GetValue($"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($"Metal.{Module}.MetalTotalWafersFaultLimit")}");
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.TotalWafers} is exceed MetalTotalWafersWarningLimit:{SC.GetValue($"Metal.{Module}.MetalTotalWafersWarningLimit")}");
                        AlarmListManager.Instance.AddWarn(Module.ToString(),
                            $"{Module.ToString()}.metalUsage.TotalWafers",
                            $"{Module.ToString()} usage:{metalUsage.TotalWafers} is exceed MetalTotalWafersWarningLimit:{SC.GetValue($"Metal.{Module}.MetalTotalWafersWarningLimit")}");
                    }
                }
                //AnodeAWafer
                if (metalUsage.AnodeAWafers > SC.GetValue($"Metal.{Module}.AnodeATotalWafersWarningLimit") && SC.GetValue($"Metal.{Module}.AnodeATotalWafersWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.AnodeATotalWafersFaultLimit") != 0)
                {
                    if (metalUsage.AnodeAWafers > SC.GetValue($"Metal.{Module}.AnodeATotalWafersFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeAWafers} is exceed AnodeATotalWafersFaultLimit:{SC.GetValue($"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($"Metal.{Module}.AnodeATotalWafersFaultLimit")}");
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeAWafers} is exceed AnodeATotalWafersWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeATotalWafersWarningLimit")}");
                        AlarmListManager.Instance.AddWarn(Module.ToString(),
                            $"{Module.ToString()}.metalUsage.AnodeAWafers",
                            $"{Module.ToString()} usage:{metalUsage.AnodeAWafers} is exceed AnodeATotalWafersWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeATotalWafersWarningLimit")}");
                    }
                }
                //AnodeBWafer
                if (metalUsage.AnodeBWafers > SC.GetValue($"Metal.{Module}.AnodeBTotalWafersWarningLimit") && SC.GetValue($"Metal.{Module}.AnodeBTotalWafersWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.AnodeBTotalWafersFaultLimit") != 0)
                {
                    if (metalUsage.AnodeBWafers > SC.GetValue($"Metal.{Module}.AnodeBTotalWafersFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeBWafers} is exceed AnodeBTotalWafersFaultLimit:{SC.GetValue($"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($"Metal.{Module}.AnodeBTotalWafersFaultLimit")}");
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeBWafers} is exceed AnodeBTotalWafersWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeBTotalWafersWarningLimit")}");
                        AlarmListManager.Instance.AddWarn(Module.ToString(),
                            $"{Module.ToString()}.metalUsage.AnodeBWafers",
                            $"{Module.ToString()} usage:{metalUsage.AnodeBWafers} is exceed AnodeBTotalWafersWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeBTotalWafersWarningLimit")}");
                    }
                }
                //AnodeAbathUsage
                if (metalUsage.AnodeABathUsage > SC.GetValue($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit") && SC.GetValue($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit") != 0)
                {
                    if (metalUsage.AnodeABathUsage > SC.GetValue($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeABathUsage} is exceed AnodeABathTotalUsageDaysFaultLimit:{SC.GetValue($"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($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit")}");
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeABathUsage} is exceed AnodeABathTotalUsageDaysWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit")}");
                        AlarmListManager.Instance.AddWarn(Module.ToString(),
                            $"{Module.ToString()}.metalUsage.AnodeABathUsage",
                            $"{Module.ToString()} usage:{metalUsage.AnodeABathUsage} is exceed AnodeABathTotalUsageDaysWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit")}");
                    }
                }
                //AnodeBbathUsage
                if (metalUsage.AnodeBBathUsage > SC.GetValue($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit") && SC.GetValue($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit") != 0)
                {
                    if (metalUsage.AnodeBBathUsage > SC.GetValue($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeBBathUsage} is exceed AnodeBBathTotalUsageDaysFaultLimit:{SC.GetValue($"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($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit")}");
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module.ToString()} usage:{metalUsage.AnodeBBathUsage} is exceed AnodeBBathTotalUsageDaysWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit")}");
                        AlarmListManager.Instance.AddWarn(Module.ToString(),
                            $"{Module.ToString()}.metalUsage.AnodeBBathUsage",
                            $"{Module.ToString()} usage:{metalUsage.AnodeBBathUsage} is exceed AnodeBBathTotalUsageDaysWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit")}");
                    }
                }
            }
        }
        /// 
        /// 获取A面使用的电量
        /// 
        /// 
        private double GetAnodeAUsage()
        {
            if (_metalItem.SubType == STRATUS)
            {
                return Math.Round(_standardHotRunRecipeRoutine.AnodeAUsage, 3);
            }
            else
            {
                return Math.Round(_compactEmbranceRunRecipeRoutine.AnodeAUsage, 3);
            }
        }
        /// 
        /// 获取B面使用的电量
        /// 
        /// 
        private double GetAnodeBUsage()
        {
            if (_metalItem.SubType == STRATUS)
            {
                return Math.Round(_standardHotRunRecipeRoutine.AnodeBUsage, 3);
            }
            else
            {
                return Math.Round(_compactEmbranceRunRecipeRoutine.AnodeBUsage, 3);
            }
        }
        /// 
        /// Abort Recipe
        /// 
        /// 
        /// 
        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
        /// 
        /// Current Short Test
        /// 
        /// 
        /// 
        private bool CurrentShortTest(object[] param)
        {
            return _currentShortTestRoutine.Start() == RState.Running;
        }
        /// 
        /// Current Short test监控
        /// 
        /// 
        /// 
        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
        /// 
        /// 关闭Flow Valve
        /// 
        /// 
        /// 
        private bool CloseFlowValve(object[] param)
        {
            if (_metalItem.SubType == STRATUS)
            {
                StandardHotMetalDevice device = DEVICE.GetDevice(Module.ToString());
                if (device != null)
                {
                    return device.SwitchToBypass("", null);
                }
                return false;
            }
            else
            {
                CompactMembranMetalDevice device = DEVICE.GetDevice(Module.ToString());
                if (device != null)
                {
                    string reservoir = ReservoirItemManager.Instance.GetReservoirByMetal(Module.ToString());
                    CompactMembranReservoirDevice reservoirDevice = DEVICE.GetDevice(reservoir);
                    if (reservoirDevice != null)
                    {
                        bool result = reservoirDevice.CAByPassOn("", null);
                        if (result)
                        {
                            return device.CellFlowValveOff("", null);
                        }
                    }
                }
                return false;
            }
        }
        #endregion
        #region Open Flow Valve
        /// 
        /// 打开Flow Valve
        /// 
        /// 
        /// 
        private bool OpenFlowValve(object[] param)
        {
            if (_metalItem.SubType == STRATUS)
            {
                StandardHotMetalDevice device = DEVICE.GetDevice(Module.ToString());
                if (device != null)
                {
                    bool result = device.SwitchToFlow("", null);
                    if (result)
                    {
                        return device.OpenPump("", null);
                    }
                }
                return false;
            }
            else
            {
                CompactMembranMetalDevice device = DEVICE.GetDevice(Module.ToString());
                if (device != null)
                {
                    string reservoir = ReservoirItemManager.Instance.GetReservoirByMetal(Module.ToString());
                    CompactMembranReservoirDevice reservoirDevice = DEVICE.GetDevice(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(eEvent.ERR_METAL, Module.ToString(), (int)MetalMsg.Initialize))
                    {
                        return (int)MetalMsg.Initialize;
                    }
                    else
                    {
                        return (int)FSM_MSG.NONE;
                    }
            }
            return (int)FSM_MSG.NONE;
        }
        /// 
        /// 监控 PM Counter Metal用量
        /// 
        private bool MetalUsageMointor(string Module)
        {
            MetalUsage metalUsage = MetalUsageManager.Instance.GetMetalUsage(Module);
            if (metalUsage != null)
            {
                //TotalAUsage
                if (metalUsage.TotalUsage > SC.GetValue($"Metal.{Module}.MetalTotalAmpHoursWarningLimit") && SC.GetValue($"Metal.{Module}.MetalTotalAmpHoursWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.MetalTotalAmpHoursFaultLimit") != 0)
                {
                    if (metalUsage.TotalUsage > SC.GetValue($"Metal.{Module}.MetalTotalAmpHoursFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module} usage:{metalUsage.TotalUsage} is exceed MetalTotalAmpHoursFaultLimit:{SC.GetValue($"Metal.{Module}.MetalTotalAmpHoursFaultLimit")}");
                        return false;
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module.ToString(), $"{Module} usage:{metalUsage.TotalUsage} is exceed MetalTotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.MetalTotalAmpHoursWarningLimit")}");
                    }
                }
                //AnodeAUsage
                if (metalUsage.AnodeAUsage > SC.GetValue($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit") && SC.GetValue($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit") != 0)
                {
                    if (metalUsage.AnodeAUsage > SC.GetValue($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module.ToString(), $"{Module} usage:{metalUsage.AnodeAUsage} is exceed AnodeATotalAmpHoursFaultLimit:{SC.GetValue($"Metal.{Module}.AnodeATotalAmpHoursFaultLimit")}");
                        return false;
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.AnodeAUsage} is exceed AnodeATotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeATotalAmpHoursWarningLimit")}");
                    }
                }
                //AnodeBUsage
                if (metalUsage.AnodeBUsage > SC.GetValue($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit") && SC.GetValue($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit") != 0)
                {
                    if (metalUsage.AnodeBUsage > SC.GetValue($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.AnodeBUsage} is exceed AnodeBTotalAmpHoursFaultLimit:{SC.GetValue($"Metal.{Module}.AnodeBTotalAmpHoursFaultLimit")}");
                        return false;
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.AnodeBUsage} is exceed AnodeBTotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeBTotalAmpHoursWarningLimit")}");
                    }
                }
                //MembraneAUsage
                if (metalUsage.MembranceAUsage > SC.GetValue($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit") && SC.GetValue($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit") != 0)
                {
                    if (metalUsage.MembranceAUsage > SC.GetValue($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.MembranceAUsage} is exceed MembraneATotalAmpHoursFaultLimit:{SC.GetValue($"Metal.{Module}.MembraneATotalAmpHoursFaultLimit")}");
                        return false;
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.MembranceAUsage} is exceed MembraneATotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.MembraneATotalAmpHoursWarningLimit")}");
                    }
                }
                //MembraneBUsage
                if (metalUsage.MembranceBUsage > SC.GetValue($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit") && SC.GetValue($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit") != 0)
                {
                    if (metalUsage.MembranceBUsage > SC.GetValue($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.MembranceBUsage} is exceed MembraneBTotalAmpHoursFaultLimit:{SC.GetValue($"Metal.{Module}.MembraneBTotalAmpHoursFaultLimit")}");
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.MembranceBUsage} is exceed MembraneBTotalAmpHoursWarningLimit:{SC.GetValue($"Metal.{Module}.MembraneBTotalAmpHoursWarningLimit")}");
                    }
                }
                //TotalWafer
                if (metalUsage.TotalWafers > SC.GetValue($"Metal.{Module}.MetalTotalWafersWarningLimit") && SC.GetValue($"Metal.{Module}.MetalTotalWafersWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.MetalTotalWafersFaultLimit") != 0)
                {
                    if (metalUsage.TotalWafers > SC.GetValue($"Metal.{Module}.MetalTotalWafersFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.TotalWafers} is exceed MetalTotalWafersFaultLimit:{SC.GetValue($"Metal.{Module}.MetalTotalWafersFaultLimit")}");
                        return false;
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.TotalWafers} is exceed MetalTotalWafersWarningLimit:{SC.GetValue($"Metal.{Module}.MetalTotalWafersWarningLimit")}");
                    }
                }
                //AnodeAWafer
                if (metalUsage.AnodeAWafers > SC.GetValue($"Metal.{Module}.AnodeATotalWafersWarningLimit") && SC.GetValue($"Metal.{Module}.AnodeATotalWafersWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.AnodeATotalWafersFaultLimit") != 0)
                {
                    if (metalUsage.AnodeAWafers > SC.GetValue($"Metal.{Module}.AnodeATotalWafersFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.AnodeAWafers} is exceed AnodeATotalWafersFaultLimit:{SC.GetValue($"Metal.{Module}.AnodeATotalWafersFaultLimit")}");
                        return false;
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.AnodeAWafers} is exceed AnodeATotalWafersWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeATotalWafersWarningLimit")}");
                    }
                }
                //AnodeBWafer
                if (metalUsage.AnodeBWafers > SC.GetValue($"Metal.{Module}.AnodeBTotalWafersWarningLimit") && SC.GetValue($"Metal.{Module}.AnodeBTotalWafersWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.AnodeBTotalWafersFaultLimit") != 0)
                {
                    if (metalUsage.AnodeBWafers > SC.GetValue($"Metal.{Module}.AnodeBTotalWafersFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.AnodeBWafers} is exceed AnodeBTotalWafersFaultLimit:{SC.GetValue($"Metal.{Module}.AnodeBTotalWafersFaultLimit")}");
                        return false;
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.AnodeBWafers} is exceed AnodeBTotalWafersWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeBTotalWafersWarningLimit")}");
                    }
                }
                //AnodeAbathUsage
                if (metalUsage.AnodeABathUsage > SC.GetValue($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit") && SC.GetValue($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit") != 0)
                {
                    if (metalUsage.AnodeABathUsage > SC.GetValue($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.AnodeABathUsage} is exceed AnodeABathTotalUsageDaysFaultLimit:{SC.GetValue($"Metal.{Module}.AnodeABathTotalUsageDaysFaultLimit")}");
                        return false;
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.AnodeABathUsage} is exceed AnodeABathTotalUsageDaysWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeABathTotalUsageDaysWarningLimit")}");
                    }
                }
                //AnodeBbathUsage
                if (metalUsage.AnodeBBathUsage > SC.GetValue($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit") && SC.GetValue($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit") != 0 && SC.GetValue($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit") != 0)
                {
                    if (metalUsage.AnodeBBathUsage > SC.GetValue($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit"))
                    {
                        LOG.WriteLog(eEvent.ERR_METAL, Module, $"{Module} usage:{metalUsage.AnodeBBathUsage} is exceed AnodeBBathTotalUsageDaysFaultLimit:{SC.GetValue($"Metal.{Module}.AnodeBBathTotalUsageDaysFaultLimit")}");
                        return false;
                    }
                    else
                    {
                        LOG.WriteLog(eEvent.WARN_METAL, Module, $"{Module} usage:{metalUsage.AnodeBBathUsage} is exceed AnodeBBathTotalUsageDaysWarningLimit:{SC.GetValue($"Metal.{Module}.AnodeBBathTotalUsageDaysWarningLimit")}");
                    }
                }
            }
            return true;
        }
    }
    public enum MetalMsg
    {
        NONE,
        Error,
        ResumeError,
        Initialize,
        Manual,
        Auto,
        CurrentShortTest,
        CloseFlowValve,
        OpenFlowValve,
        RunRecipe,
        Abort,
        Init
    }
}