using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Device;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.RT.RecipeCenter;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using MECF.Framework.Common.Beckhoff.ModuleIO;
using MECF.Framework.Common.CommonData.Reservoir;
using MECF.Framework.Common.Persistent.Reservoirs;
using MECF.Framework.Common.RecipeCenter;
using MECF.Framework.Common.ToolLayout;
using CyberX8_Core;
using CyberX8_RT.Devices.Facilities;
using CyberX8_RT.Devices.Metal;
using CyberX8_RT.Modules.Reservoir;
using System;
using System.Collections.Generic;
using System.Reflection;
using CyberX8_RT.Modules;
using System.Collections.ObjectModel;
using CyberX8_RT.Devices.Dose;
using CyberX8_RT.Modules.Metal;
using CyberX8_RT.Devices.Temperature;
using CyberX8_RT.Devices.PowerSupplier;
using System.Linq;
using CyberX8_RT.Devices.Safety;
using MECF.Framework.Common.ProcessCell;
using MECF.Framework.Common.Alarm;
using MECF.Framework.Common.IOCore;
using static CyberX8_RT.Devices.Reservoir.ReservoirDiReplenHelper;
namespace CyberX8_RT.Devices.Reservoir
{
    public class StandardHotReservoirDevice : BaseDevice, IDevice
    {
        private enum ReservoirOperation
        {
            None,
            ManualDiReplen,
            AutoDiReplen
        }
        private enum DosingOperation
        {
            None,
            ManualDosing,
            AutoDosing,
        }
        #region 常量
        private const string AUTO = "Auto";
        private const string MANUAL = "Manual";
        private const string DISABLE = "Disable";
        private const string STRATUS = "Stratus";
        private const string PERSISTENT_VALUE = "PersistentValue";
        private const string FLOW = "Flow";
        private const string HED_FLOW = "HedFlow";
        private const string PH_FLOW_VALVE = "PHFlowValve";
        private const string PH_VALUE = "PHValue";
        private const string WATER_LEVEL = "WaterLevel";
        private const string LOW_LEVEL = "LowLevel";
        private const string HIGH_LEVEL = "HighLevel";
        private const string SAFETY_HIGH_LEVEL = "SafetyHighLevel";
        private const string DI_REPLEN = "DiReplen";
        private const string RESERVOIRDEVICEDATA = "ReservoirDeviceData";
        private const string REPLEN_LEVEL = "ReplenLevel";
        private const string RESPOWERON = "ResPowerOn";
        private const string HEDPOWERON = "HedPowerOn";
        private const double PUMP_SPEED_CONVERT = 0.0672;
        private const int ENABLE = 5;
        private const string REGULATE_PUMP = "RegulatePumpOn";
        private const string REGULATE_PUMP_SIGNAL_IN = "RegulatePumpSignalIn";
        private const string REGULATE_PUMP_SPEED = "RegulatePumpSpeed";
        #endregion
        #region 内部变量
        /// 
        /// Level取样平均值
        /// 
        private double _avgLevel;
        /// 
        /// AN Level取样队列
        /// 
        private Queue _LevelSamples;
        /// 
        /// Level计算平均值取样数
        /// 
        private int levelSampleCount;
        /// 
        /// Prewet 持久性数值对象
        /// 
        private ReservoirsPersistentValue _persistentValue;
        /// 
        /// 定时器Job
        /// 
        PeriodicJob _periodicJob = null;
        /// 
        /// 变量是否初始化字典
        /// 
        private Dictionary _variableInitializeDic = new Dictionary();
        /// 
        /// Reservoir数据
        /// 
        private StandardHotReservoirData _reservoirData = new StandardHotReservoirData();
        /// 
        /// 当前操作
        /// 
        private ReservoirOperation _currentOperation = ReservoirOperation.None;
        /// 
        /// 手动注水时间(秒)
        /// 
        private int _manualReplenSecond = 0;
        /// 
        /// 注水是否出错
        /// 
        private bool _isDiReplenInFault = false;
        /// 
        /// Recipe
        /// 
        private ResRecipe _resRecipe;
        /// 
        /// 平均PH值
        /// 
        private double _avaragePH;
        /// 
        /// PH Routine
        /// 
        private StandardHotPHRoutine _phRoutine;
        /// 
        /// PH Routine状态
        /// 
        private RState _phState = RState.Init;
        /// 
        /// PH结束时间
        /// 
        private DateTime _phRoutineEndTime = DateTime.Now;
        /// 
        /// Direplen 逻辑对象
        /// 
        private ReservoirDiReplenHelper _direplenHelper;
        /// 
        /// Replen数量
        /// 
        private int _replenNum = 0;
        /// 
        /// Dose Replen数据
        /// 
        private ReplenData[] _replenDatas;
        /// 
        /// ReplenType
        /// 
        private string _replenType;
        /// 
        /// Replen Recipe集合
        /// 
        private RdsRecipe[] _rdsRecipe;
        /// 
        /// DosingSystemHelper对象列表
        /// 
        private List _dosingSystemHelperLst;
        /// 
        /// ReplenLevel 列表
        /// 
        private List _replenLevelLst;
        /// 
        /// 当前操作
        /// 
        private List _currentDosingOperation;
        /// 
        /// 配置的metal device集合
        /// 
        private ObservableCollection _metalDevices = new ObservableCollection();
        /// 
        /// Replen Persistent Value
        /// 
        private Dictionary _replenPersistentValue = new Dictionary();
        /// 
        /// DosingCommonHelper
        /// 
        private DosingCommonHelper _dosingCommonHelper;
        /// 
        /// WarningFlag
        /// 
        private List _isCAFlowRateWARN;
        /// 
        /// CMM Flow High Error
        /// 
        private double _reservoirCMMFlowHighError;
        /// 
        /// CMM Flow Low Error
        /// 
        private double _reservoirCMMFlowLowError;
        /// 
        /// ErrorMessage
        /// 
        private bool _isTCControlWARN = false;
        private bool _isCMMPowerCurrentWARN = false;
        private bool _isCMMPowerFlowWARN = false;
        private bool _isExportCMMUsage = false;
        private bool _isAutoDIReplenError = false;
        private bool _isSafetyHigh = false;
        private bool _isSystemAutoMode = false;
        /// 
        /// 用于控制打印错误log
        /// 
        private HashSet errorLogSet = new HashSet();
        /// 
        /// 是否为调速类型的pump
        /// 
        private bool _isRegulatePump = false;
        /// 
        /// 调速泵Pump速度
        /// 
        private double _regulatePumpSpeed;
        /// 
        /// 泵速Helper
        /// 
        private ReservoirPumpSpeedHelper _pumpSpeedHelper;
        /// 
        /// 是否配备CMM
        /// 
        private bool _isCMMConfig = false;
        /// 
        /// holdofftime 配置项
        /// 
        private double _cellFlowHoldoffTime;
        /// 
        /// holdofftimeMonitor字典;key:Moudle_item,value :HoldoffTimeSignalMonitor对象
        /// 
        private Dictionary _holdoffTimeSignalDic = new Dictionary();
        /// 
        /// 累计补水超时
        /// 
        private bool _diReplenMaxTimeOut;
        /// 
        /// 是否有其他DIReplen的警告
        /// 
        private bool _isOtherDIReplenWarnOn = false;
        /// 
        /// Facility DIReplen Off Warning
        /// 
        private bool _isTotalDIReplenWarnOn = false;
        #endregion
        #region 属性
        /// 
        /// 数据
        /// 
        public StandardHotReservoirData ReservoirData { get { return _reservoirData; } }
        /// 
        /// Replen数据
        /// 
        public ReplenData[] ReplenDatas { get { return _replenDatas; } }
        /// 
        /// DosingSystemHelper对象列表
        /// 
        public List DosingSystemHelpers { get { return _dosingSystemHelperLst; } }
        /// 
        /// Replen数量
        /// 
        public int ReplenNum { get { return _replenNum; } }
        /// 
        /// 操作模式
        /// 
        public string OperationMode { get { return _persistentValue.OperatingMode; } }
        /// 
        /// 工程模式
        /// 
        public string EngineerMode { get { return _persistentValue.RecipeOperatingMode; } }
        /// 
        /// 是否自动
        /// 
        public bool IsAuto { get { return _persistentValue.OperatingMode == AUTO; } }
        /// 
        /// 是否需要补水
        /// 
        public bool NeedAutoDireplen { get { return GetNeedAutoDireplen(); } }
        /// 
        /// 正在补水
        /// 
        public bool IsDireplenOn { get { return _reservoirData.DiReplen; } }
        /// 
        /// 平均PH数值
        /// 
        public double AveragePH { get { return _avaragePH; } set { _avaragePH = value; } }
        /// 
        /// 当前Recipe
        /// 
        public ResRecipe Recipe { get { return _resRecipe; } }
        /// 
        /// 当前Rds Recipe
        /// 
        public RdsRecipe[] RdsRecipe { get { return _rdsRecipe; } }
        /// 
        /// 是否调速泵
        /// 
        public bool IsRegulatePump { get { return _isRegulatePump; } }
        /// 
        /// 补水累计超时
        /// 
        public bool DiReplenMaxTimeOut
        {
            get { return _diReplenMaxTimeOut; }
        }
        /// 
        /// 每隔30秒打印槽体相关的信息
        /// 
        private DateTime _periodLogTime;
        /// 
        /// Facility DIReplenOn
        /// 
        public bool TotalDIReplenOn
        {
            get
            {
                var facilitiesDevice = DEVICE.GetDevice("System.Facilities");
                return facilitiesDevice != null ? facilitiesDevice.DIReplenEnable : false;
            }
        }
        /// 
        /// 注水是否出错
        /// 
        public bool IsDiReplenInFault { get { return _isDiReplenInFault; } }
        #endregion
        /// 
        /// 初始化成功清除对应的错误log
        /// 
        /// 
        public void ClearErrorLogSet(string module)
        {
            // 使用构造函数复制 HashSet
            HashSet newHashSet = new HashSet(errorLogSet);
            foreach (var item in newHashSet)
            {
                if (item.Contains(module))
                {
                    errorLogSet.Remove(item);
                }
            }
        }
        /// 
        /// 构造函数
        /// 
        /// 
        /// 
        public StandardHotReservoirDevice(string moduleName) : base(moduleName, moduleName, moduleName, moduleName)
        {
            levelSampleCount = SC.GetValue("Reservoir.LevelAvgSamples");
            levelSampleCount = levelSampleCount == 0 ? 20 : levelSampleCount;
            _LevelSamples = new Queue(levelSampleCount);
            _cellFlowHoldoffTime = SC.GetValue($"Metal.CellFlowAlarmHoldoffTime");
            _periodicJob = new PeriodicJob(100, OnTimer, $"{Module}.OnTimer", true);
            ResPowerOn();//保持继电器常开
            ClearErrorLogSet(Module.ToString());//软件启动的时候也清一下ErrorLogSet
            _periodLogTime = DateTime.Now;
        }
        /// 
        /// 定时器
        /// 
        /// 
        private bool OnTimer()
        {
            if ((DateTime.Now - _periodLogTime).TotalSeconds > 30)
            {
                _periodLogTime = DateTime.Now;
                double temp = 0;
                ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(Module.ToString());
                if (reservoirItem != null)
                {
                    TemperatureController temperatureController = DEVICE.GetDevice(reservoirItem.TCID);
                    if (temperatureController != null)
                    {
                        temp = temperatureController.TemperatureData.ReserviorTemperature;
                    }
                }
                for (int i = 0; i < _metalDevices.Count; i++)
                {
                    StandardHotMetalDevice hotMetalDevice = _metalDevices[i];
                    if (hotMetalDevice != null)
                    {
                        LOG.WriteLog(eEvent.INFO_METAL, hotMetalDevice.Name, $"CA Flow:{hotMetalDevice.MetalDeviceData.CellFlow} L/min" +
                            $" PumpEnable : {hotMetalDevice.MetalDeviceData.CellPump}  " +
                            $" Circulation : {hotMetalDevice.MetalDeviceData.Circulation}  ");
                    }
                }
                LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"{Module} PH :{_reservoirData.PHValue},  " +
                                $"Temp:{temp}C  " +
                                $"CALevel = {_reservoirData.Level}L  ");
            }
            //计算AN/CA level的平均值
            if (ReservoirData != null)
            {
                //AN
                if (_LevelSamples.Count >= levelSampleCount)
                {
                    _LevelSamples.Dequeue();
                    _LevelSamples.Enqueue(ReservoirData.Level);
                }
                else
                {
                    _LevelSamples.Enqueue(ReservoirData.Level);
                }
                _avgLevel = _LevelSamples.Count > 0 ? _LevelSamples.Average() : 0;
            }
            foreach (StandardHotMetalDevice device in _metalDevices)
            {
                device.OnTimer(_periodicJob.Interval);
            }
            /// 
            /// WaterLevelMonitor
            /// 
            WaterLevelMonitor();
            //TC SafeCheck
            TCSafeCheck();
            //DIReplen总阀未开关闭槽体DIReplen;未初始化关闭槽体DIReplen
            ReservoirEntity reservoirEntity = Singleton.Instance.GetModule(Module);
            SystemFacilities systemFacility = DEVICE.GetDevice("System.Facilities");
            if (systemFacility!=null && (!systemFacility.DIReplenEnable || (reservoirEntity == null || !reservoirEntity.IsInitialized)))
            {
                if (IsDireplenOn) DIReplenOff("", null);
            }
            
            if (reservoirEntity == null || !reservoirEntity.IsInitialized)
            {
                return true;
            }
            //DIReplen
            if (_direplenHelper != null)
            {
                _direplenHelper.MonitorPeriodTime(DiReplenTimeOutOpeartion);
                if (!_diReplenMaxTimeOut && !_isDiReplenInFault && TotalDIReplenOn)
                {
                    if (_currentOperation == ReservoirOperation.ManualDiReplen && _reservoirData.DiReplen)
                    {
                        bool result = _direplenHelper.MonitorManualDiReplenComplete(_manualReplenSecond, DIReplenOff, DiReplenTimeOutOpeartion);
                        if (result)
                        {
                            _currentOperation = ReservoirOperation.None;
                        }
                    }
                    else if (_currentOperation == ReservoirOperation.AutoDiReplen && _reservoirData.DiReplen)
                    {
                        var result = _direplenHelper.AutoDiReplenMonitorTimeOut(DIReplenOff, DiReplenTimeOutOpeartion);
                        if (result.Item1)
                        {
                            _currentOperation = ReservoirOperation.None;
                            //触发注水异常信号
                            if (result.Item2 == DIReplenFaultType.PerFillTimeOut) _isDiReplenInFault = true;
                        }
                        else
                        {
                            //按液位补水
                            var result1 = _direplenHelper.AutoDiReplenMonitorComplete(_reservoirData.Level, _resRecipe.ReservoirCALevel, _resRecipe.DIReplenEnable,
                            _resRecipe.DIReplenTimeRate, _resRecipe.DIReplenCurrentRate, DIReplenOff);
                            if (result1)
                            {
                                //补水完成打印日志
                                LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"{Module} CADIReplen State :False,  " +
                                $"SetPoint = {Recipe.ReservoirCALevel}  " +
                                $"Level = {_reservoirData.Level}  " +
                                $"HighLimit = {Recipe.CALevelErrorHigh}  " +
                                $"LowLimit = {Recipe.CALevelErrorLow}  ");
                                _currentOperation = ReservoirOperation.None;
                            }
                        }
                    }
                }
            }
            if (_persistentValue.OperatingMode == AUTO)
            {
                CAFlowRateCheck();
                TemperatureCheck();
                CMMPowerCheck();
                //CMM用电量记录                         
                if (_isExportCMMUsage && _persistentValue.CMMStartTime != DateTime.MinValue)
                {
                    ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(Module);
                    if (!string.IsNullOrEmpty(reservoirItem.CMMSupplyID) && _resRecipe != null && _resRecipe.CMMEnable)
                    {
                        CellPowerSupplier powerSupplier = DEVICE.GetDevice(reservoirItem.CMMSupplyID);
                        double cmmUsage = powerSupplier.PowerSupplierData.Current * DateTime.Now.Subtract(_persistentValue.CMMStartTime).TotalHours;
                        ReservoirUsageManager.Instance.UpdateReservoirCMMUsage(Module, Math.Round(cmmUsage, 3));
                        _persistentValue.CMMStartTime = DateTime.Now;
                        ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
                        _isExportCMMUsage = false;
                    }
                }
                if (_isRegulatePump && _pumpSpeedHelper != null)
                {
                    _pumpSpeedHelper.Monitor(_resRecipe);//泵调速
                }
            }
            if(ReservoirItemManager.Instance.GetReservoirItem(Module)!=null && !string.IsNullOrEmpty(ReservoirItemManager.Instance.GetReservoirItem(Module).PHProbeType))
            {
                if (_phState == RState.Running)
                {
                    _phState = _phRoutine.Monitor();
                    if (_phState == RState.End)
                    {
                        _phRoutineEndTime = DateTime.Now;
                    }
                }
                else if (_phState != RState.Init)
                {
                    double phUpdatePeriod = SC.GetValue("Reservoir.PHUpdatePeriod");
                    if (DateTime.Now.Subtract(_phRoutineEndTime).TotalMilliseconds >= phUpdatePeriod * 1000)
                    {
                        StartDetectPHValve();
                    }
                }
            }
            return true;
        }
        
        /// 
        /// Low Level 触发操作
        /// 
        private void LowLevelOperation()
        {
            if (ReservoirData.WaterLevel < SC.GetValue($"Reservoir.{Module}.LowLevel"))  
            {
                ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(Module);
                foreach (var metalDevice in _metalDevices)
                {
                    if (metalDevice.MetalDeviceData.Circulation)
                    {
                        metalDevice.SwitchToBypass($"{metalDevice.Name}.SwitchToBypass", null);
                    }
                    if (metalDevice.MetalDeviceData.CellFlow > 0)
                    {
                        metalDevice.ClosePump("", null);
                    }
                    //水位过低时将起对应的metal也要切成error
                    MetalEntity metalEntity = Singleton.Instance.GetModule(metalDevice.DeviceID);
                    if (metalEntity != null && !metalEntity.IsError)
                    {
                        metalEntity.PostMsg(MetalMsg.Error);
                    }
                }
                //禁用TC
                if (!String.IsNullOrEmpty(reservoirItem.TCID))
                {
                    TemperatureController temperatureController = DEVICE.GetDevice(reservoirItem.TCID);
                    if (temperatureController != null && temperatureController.TemperatureData.ControlOperationModel == 5)
                    {
                        LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Lowlevel was activate");
                        temperatureController.DisableOperation("", null);
                    }
                }
            }
        }
        /// 
        /// 检验TC的运行状态是否满足
        /// 
        private void TCSafeCheck()
        {
            bool result = false;
            double totalFlow = 0;
            //检验冷却水
            SystemFacilities systemFacilities = DEVICE.GetDevice("System.Facilities");
            if ((systemFacilities != null && !systemFacilities.HouseChilledWaterEnable))
            {
                result = true;
            }
            //检验循环流量
            for (int i = 0; i < _metalDevices.Count; i++)
            {
                StandardHotMetalDevice hotMetalDevice = _metalDevices[i];
                if (hotMetalDevice != null)
                {
                    if (hotMetalDevice.MetalDeviceData != null)
                    {
                        totalFlow += hotMetalDevice.MetalDeviceData.CellFlow;
                    }
                }
            }
            if(totalFlow <= 0)
            {
                result = true ;
            }       
            if (result)
            {
                ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(Module);
                //禁用TC
                if (!String.IsNullOrEmpty(reservoirItem.TCID))
                {
                    TemperatureController temperatureController = DEVICE.GetDevice(reservoirItem.TCID);
                    if (temperatureController != null && temperatureController.TemperatureData.ControlOperationModel == 5)
                    {
                        LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"TC run status abnormal, Disable TC !");
                        temperatureController.DisableOperation("", null);
                    }
                }
            }
        }
        /// 
        /// High Level 触发操作
        /// 
        private void HighLevelOperation()
        {
            SystemFacilities systemFacilities = DEVICE.GetDevice("System.Facilities");
            if (systemFacilities != null)
            {
                if (systemFacilities.DIFillEnable) systemFacilities.DiFillDisableOperation("DIFillDisableOpeartion", null);
                if (systemFacilities.DIReplenEnable) systemFacilities.DiReplenDisableOperation("DiReplenDisableOperation", null);
                if (ReservoirData.DiReplen)
                {
                    _currentOperation = ReservoirOperation.None;
                    DIReplenOff("", null);
                }
            }
        }
        /// 
        /// WaterLevelMonitor
        /// 
        private void WaterLevelMonitor()
        {
            ReservoirEntity reservoirEntity = Singleton.Instance.GetModule(Module);
            _isSystemAutoMode = reservoirEntity.IsAuto;
            //触发水位过高或者过低将reservoir切成error
            if (ReservoirData.WaterLevel < SC.GetValue($"Reservoir.{Module}.LowLevel"))
            {
                if (!errorLogSet.Contains($"{Module}.WaterLevel"))
                {
                    errorLogSet.Add($"{Module}.WaterLevel");
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"WaterLevel low is Activate");
                }
                LowLevelOperation();
                if (!reservoirEntity.IsError)
                {
                    reservoirEntity.PostMsg(ReservoirMsg.Error);
                }
                if (_isSystemAutoMode && !AlarmListManager.Instance.IsContainDataError(Module, "WaterLevel"))
                {
                    AlarmListManager.Instance.AddDataError(Module,
                        $"WaterLevel", $"WaterLevel low is Activate");
                }
            }
            else if (ReservoirData.WaterLevel > SC.GetValue($"Reservoir.{Module}.HighLevel"))
            {
                if (!errorLogSet.Contains($"{Module}.WaterLevel"))
                {
                    errorLogSet.Add($"{Module}.WaterLevel");
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"WaterLevel:{ReservoirData.WaterLevel} is larger than  HighLevel Config:{SC.GetValue($"Reservoir.{Module}.HighLevel")}");
                }
                HighLevelOperation();
                if (!reservoirEntity.IsError)
                {
                    reservoirEntity.PostMsg(ReservoirMsg.Error);
                }
                if (_isSystemAutoMode && !AlarmListManager.Instance.IsContainDataError(Module, "WaterLevel"))
                {
                    AlarmListManager.Instance.AddDataError(Module,
                        $"WaterLevel", $"WaterLevel:{ReservoirData.WaterLevel} is larger than  HighLevel Config:{SC.GetValue($"Reservoir.{Module}.HighLevel")}");
                }
            }
            //High high监控
            if (ReservoirData.SafetyHighLevel)
            {
                if (!errorLogSet.Contains($"{Module}.SafetyHigh"))
                {
                    errorLogSet.Add($"{Module}.SafetyHigh");
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Safety High is activate");
                }
                HighLevelOperation();
                if (!reservoirEntity.IsError)
                {
                    reservoirEntity.PostMsg(ReservoirMsg.Error);
                }
                if (_isSystemAutoMode && !AlarmListManager.Instance.IsContainDataError(Module, "SafetyHigh"))
                {
                    AlarmListManager.Instance.AddDataError(Module,
                        $"SafetyHigh", $"Safety High is activate");
                }
            }
            //水位触发reservoir里面的high/low将对应的reservoir切成error
            if (_resRecipe == null) return;
            if (ReservoirData.Level < _resRecipe.CALevelErrorLow)
            {
                if (!errorLogSet.Contains($"{Module}.Level"))
                {
                    errorLogSet.Add($"{Module}.Level");
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Current level:{ReservoirData.Level} is lower than recipe's CA Errorlow paramater:{_resRecipe.CALevelErrorLow}");
                }
                if (!reservoirEntity.IsError)
                {
                    reservoirEntity.PostMsg(ReservoirMsg.Error);
                }
                if (_isSystemAutoMode && !AlarmListManager.Instance.IsContainDataError(Module, "Level"))
                {
                    AlarmListManager.Instance.AddDataError(Module,
                        $"Level", $"Current level:{ReservoirData.Level} is lower than recipe's CA Errorlow paramater:{_resRecipe.CALevelErrorLow}");
                }
            }
            else if (ReservoirData.Level > _resRecipe.CALevelErrorHigh)
            {
                if (!errorLogSet.Contains($"{Module}.Level"))
                {
                    errorLogSet.Add($"{Module}.Level");
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Current level:{ReservoirData.Level} is larger than recipe's CA ErrorHigh paramater:{_resRecipe.CALevelErrorHigh}");
                }
                if (!reservoirEntity.IsError)
                {
                    reservoirEntity.PostMsg(ReservoirMsg.Error);
                }
                if (_isSystemAutoMode && !AlarmListManager.Instance.IsContainDataError(Module, "Level"))
                {
                    AlarmListManager.Instance.AddDataError(Module,
                        $"Level", $"Current level:{ReservoirData.Level} is larger than recipe CA ErrorHigh paramater:{_resRecipe.CALevelErrorHigh}");
                }
            }
        }
        /// 
        /// 获取是否需要补水
        /// 
        /// 
        private bool GetNeedAutoDireplen()
        {
            if (IsAuto && _resRecipe != null)
            {
                if (_resRecipe.DIReplenEnable && _resRecipe.DIReplenCurrentRate == 0 && _resRecipe.DIReplenTimeRate == 0)
                {
                    double levelHysteresis = SC.GetValue("Reservoir.LevelHysteresis");
                    return ReservoirData.Level < _resRecipe.ReservoirCALevel - levelHysteresis;
                }
                return false;
            }
            else
            {
                return false;
            }
        }
        /// 
        /// 初始化
        /// 
        /// 
        public bool Initialize()
        {
            InitializeRoutine();
            InitializeParameter();
            SubscribeData();
            InitializeOperation();
            SubscribeValueAction();          
            return true;
        }
        /// 
        /// 初始化routine
        /// 
        private void InitializeRoutine()
        {
            _phRoutine = new StandardHotPHRoutine(Module);
        }
        /// 
        /// 初始化参数
        /// 
        private void InitializeParameter()
        {
            _persistentValue = ReservoirsPersistentManager.Instance.GetReservoirsPersistentValue(Module.ToString());
            if (_persistentValue == null)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module.ToString(), "Persistent Value Object is not exist");
            }
            else
            {
                if (!string.IsNullOrEmpty(_persistentValue.Recipe))
                {
                    _resRecipe = RecipeFileManager.Instance.LoadGenericityRecipe(_persistentValue.Recipe);
                }
                _direplenHelper = new ReservoirDiReplenHelper(Module, _persistentValue);
            }
            ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(Module.ToString());
            if (reservoirItem != null)
            {
                foreach (var item in reservoirItem.MetalCells)
                {
                    if (item.ModuleName != Module)
                    {
                        StandardHotMetalDevice metalDevice = DEVICE.GetDevice(item.ModuleName);
                        if (metalDevice != null)
                        {
                            _metalDevices.Add(metalDevice);
                            _holdoffTimeSignalDic.Add($"{item.ModuleName}_CellFlow", new HoldoffTimeSignalMonitor(item.ModuleName, "CellFlow"));
                        }
                    }
                }
                _isRegulatePump = "Regulate" == reservoirItem.PumpType;
                _regulatePumpSpeed = SC.GetValue("Reservoir.DefaultPumpSpeed");
                if (_isRegulatePump)
                {
                    _pumpSpeedHelper = new ReservoirPumpSpeedHelper(Module, this);
                }
                _isCMMConfig = !string.IsNullOrEmpty(reservoirItem.CMMType);
            }
            _isCAFlowRateWARN = new List(new bool[_metalDevices.Count]);
            //DosingSystem数据初始化            
            _replenType = reservoirItem.ChemReplenType;
            _replenNum = reservoirItem.ChemReplenPumps;
            if (_replenType != "" && _replenNum != 0)
            {
                _replenDatas = new ReplenData[_replenNum];
                _rdsRecipe = new RdsRecipe[_replenNum];
                _replenLevelLst = new List(new bool[_replenNum]);
                _currentDosingOperation = new List();
                for (int i = 0; i < _replenNum; i++)
                {
                    string replenName = "Replen" + (i + 1).ToString();
                    _replenDatas[i] = new ReplenData();
                    _replenDatas[i].ReplenName = replenName;
                    _replenDatas[i].RecipeName = "";
                    _replenDatas[i].BottleLevel = DosingSystemHelper.BottleLevelState.Empty.ToString();
                    _replenDatas[i].IsAutoDosingError = false;
                    if (_dosingSystemHelperLst == null) _dosingSystemHelperLst = new List();
                    _dosingSystemHelperLst.Add(new DosingSystemHelper(Module, _replenDatas[i].ReplenName));
                    _currentDosingOperation.Add(DosingOperation.None);
                    _replenPersistentValue[replenName] = ReplenPersistentManager.Instance.GetReplenPersistentValue(Module, replenName);
                }
                _dosingCommonHelper = new DosingCommonHelper(Module, _replenNum);
            }
            
        }
        /// 
        /// 订阅数据
        /// 
        private void SubscribeData()
        {
            DATA.Subscribe($"{Module}.DIValveMaxOnTime", () => SC.GetValue($"Reservoir.{Module}.DIValveMaxOnTime") * 60, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.IsManualReplen", () => { return _currentOperation == ReservoirOperation.ManualDiReplen; }, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.ReservoirData", () => _reservoirData, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.ReservoirAverageLevel", () => _avgLevel, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.{PERSISTENT_VALUE}", () => _persistentValue, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.ReplenPersistentValue", () => _replenPersistentValue, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.PHValue", () => _avaragePH, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.CurrentRecipe", () => _resRecipe, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.ReplenType", () => _replenType, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.ReplenNum", () => _replenNum, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.ReplenDatas", () => _replenDatas, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.DosingSystemState", () => _dosingCommonHelper != null ? _dosingCommonHelper.DosingSystemState : null, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.RecipeName", () => (_resRecipe != null ? _resRecipe.Ppid : ""), SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.HedFlow", () => _reservoirData.HedFlow, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.PHEnable", () => _reservoirData.PHFlowValve, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.IsDIReplenInFault", () => _isDiReplenInFault, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.ReservoirPowerOn", () => _reservoirData.ResPowerOn, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.HedPowerOn", () => _reservoirData.HedPowerOn, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.IsRegulatePump", () => _isRegulatePump, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.PumpSpeed", () => _regulatePumpSpeed, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.IsCMMConfig", () => _isCMMConfig, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.SubordinateReservoirPump", () => _reservoirData.RegulatePumpSignalIn, SubscriptionAttribute.FLAG.IgnoreSaveDB);
            DATA.Subscribe($"{Module}.DIReplenMaxTimeOut", () => _diReplenMaxTimeOut, SubscriptionAttribute.FLAG.IgnoreSaveDB);
        }
        /// 
        /// 初始化操作
        /// 
        private void InitializeOperation()
        {
            OP.Subscribe($"{Module}.DisabledAction", DisabledOperation);
            OP.Subscribe($"{Module}.ManualAction", ManualOperation);
            OP.Subscribe($"{Module}.AutoAction", AutoOperation);
            OP.Subscribe($"{Module}.EngineeringModeAction", EngineeringModeOperation);
            OP.Subscribe($"{Module}.ProductionModeAction", ProductionModeOperation);
            OP.Subscribe($"{Module}.DiReplenOn", DIReplenOnOperation);
            OP.Subscribe($"{Module}.DiReplenOff", DIReplenOff);
            OP.Subscribe($"{Module}.ManualDiReplen", ManualDiReplen);
            OP.Subscribe($"{Module}.ResetTotalTime", ResetTotalTime);
            OP.Subscribe($"{Module}.LoadRecipe", LoadRecipeOperation);
            OP.Subscribe($"{Module}.LoadDosingRecipe", LoadDosingRecipeOperation);
            OP.Subscribe($"{Module}.ReplenPumpOn", ReplenPumpOnOperation);
            OP.Subscribe($"{Module}.ReplenPumpOff", ReplenPumpOffOperation);
            OP.Subscribe($"{Module}.SetPumpFactor", SetPumpFactor);
            OP.Subscribe($"{Module}.ManualDosing", ManualDosing);
            OP.Subscribe($"{Module}.StopManualDosing", StopManualDosing);
            OP.Subscribe($"{Module}.ResetBottleVolume", ResetBottleVolume);
            OP.Subscribe($"{Module}.DosingInitialize", DosingInitialize);
            OP.Subscribe($"{Module}.DIReplenClearError", DIReplenClearError);
            OP.Subscribe($"{Module}.HedPowerOn", HedPowerOnAction);
            OP.Subscribe($"{Module}.HedPowerOff", HedPowerOffAction);
            OP.Subscribe($"{Module}.ResPowerOn", ResPowerOnAction);
            OP.Subscribe($"{Module}.ResPowerOff", ResPowerOffAction);
            OP.Subscribe($"{Module}.RegulatPumpSpeed", RegulatePumpSpeedOperation);
            OP.Subscribe($"{Module}.RegulatPumpOn", RegulatePumpOn);
            OP.Subscribe($"{Module}.RegulatPumpOff", RegulatePumpOff);
        }
        #region Regulat Pump
        /// 
        /// 调速pump
        /// 
        /// 
        /// 
        /// 
        private bool RegulatePumpSpeedOperation(string cmd, object[] args)
        {
            double MaxPumpSpeed = 0;
            if (SC.ContainsItem("Reservoir.MaxPumpSpeed"))
            {
                MaxPumpSpeed = SC.GetValue("Reservoir.MaxPumpSpeed");
            }
            if (double.TryParse(args[0].ToString(), out double speed))
            {
                _regulatePumpSpeed = speed;
                if (_regulatePumpSpeed > MaxPumpSpeed)
                {
                    _regulatePumpSpeed = MaxPumpSpeed;//设置成最大泵速
                    LOG.WriteLog(eEvent.WARN_METAL, Module, $"Pump speed:{_regulatePumpSpeed} is over max pump speed {MaxPumpSpeed}!");
                    return false;
                }
                return RegulatePumpSpeed(_regulatePumpSpeed);
            }
            else
            {
                LOG.WriteLog(eEvent.ERR_METAL, Module, $"{args[0]} is nor invalid speed");
                return false;
            }
        }
        /// 
        /// 设置泵速
        /// 
        /// 
        /// 
        public bool RegulatePumpSpeed(double PumpSpeed)
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{REGULATE_PUMP_SPEED}");
            return IOModuleManager.Instance.WriteIoValue(ioName, PumpSpeed);
        }
        /// 
        /// Pump On
        /// 
        /// 
        /// 
        /// 
        public bool RegulatePumpOn(string cmd, object[] args)
        {
            string enableIOName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{REGULATE_PUMP}");
            return IOModuleManager.Instance.WriteIoValue(enableIOName, true);
        }
        /// 
        /// Pump Off
        /// 
        /// 
        /// 
        /// 
        public bool RegulatePumpOff(string cmd, object[] args)
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{REGULATE_PUMP}");
            return IOModuleManager.Instance.WriteIoValue(ioName, false);
        }
        #endregion
        private bool HedPowerOnAction(string cmd, object[] args)
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{HEDPOWERON}");
            if (!string.IsNullOrEmpty(ioName))
            {
                bool result = IOModuleManager.Instance.WriteIoValue(ioName, true);
                if (!result)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Open HED Power Relay failed");
                    return false;
                }
                return true;
            }
            else
            {
                return true;
            }
        }
        private bool HedPowerOffAction(string cmd, object[] args)
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{HEDPOWERON}");
            if (!string.IsNullOrEmpty(ioName))
            {
                bool result = IOModuleManager.Instance.WriteIoValue(ioName, false);
                if (!result)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Close HED Power Relay failed");
                    return false;
                }
                return true;
            }
            else
            {
                return true;
            }
        }
        private bool ResPowerOnAction(string cmd, object[] args)
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{RESPOWERON}");
            if (!string.IsNullOrEmpty(ioName))
            {
                bool result = IOModuleManager.Instance.WriteIoValue(ioName, true);
                if (!result)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Open Res Power Relay failed");
                    return false;
                }
                return true;
            }
            else
            {
                return true;
            }
        }
        private bool ResPowerOffAction(string cmd, object[] args)
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{RESPOWERON}");
            if (!string.IsNullOrEmpty(ioName))
            {
                bool result = IOModuleManager.Instance.WriteIoValue(ioName, false);
                if (!result)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Close Res Power Relay failed");
                    return false;
                }
                return true;
            }
            else
            {
                return true;
            }
        }
        #region Operation
        /// 
        /// DIReplen Clear Error
        /// 
        /// 
        /// 
        private bool DIReplenClearError(string cmd, object[] args)
        {
            _isDiReplenInFault = false;
            return true;
        }
        /// 
        /// DI Replen超时
        /// 
        private void DiReplenTimeOutOpeartion(bool timeOutFlag)
        {
            _diReplenMaxTimeOut = timeOutFlag;
        }
        /// 
        /// 重置时长
        /// 
        /// 
        /// 
        /// 
        private bool ResetTotalTime(string cmd, object[] objs)
        {
            _diReplenMaxTimeOut = false;
            _persistentValue.TotalReplen = 0;
            _persistentValue.LastTotalReplen = 0;
            ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
            return true;
        }
        /// 
        /// DisabledAction
        /// 
        /// 
        /// 
        /// 
        private bool DisabledOperation(string cmd, object[] args)
        {
            string currentOperation = "Disabled";
            if (args.Length >= 1 && (bool)args[0])
            {
                foreach (var replenData in ReplenDatas)
                {
                    if (_replenPersistentValue != null && _replenPersistentValue[replenData.ReplenName] != null)
                    {
                        _replenPersistentValue[replenData.ReplenName].OperatingMode = currentOperation;
                        ReplenPersistentManager.Instance.UpdatePersistentValue(Module, replenData.ReplenName);
                    }
                }
                for (int i = 0; i < _replenNum; i++)
                {
                    string replenName = "Replen" + (i + 1).ToString();
                    if (_replenPersistentValue[replenName].IsDosingRunning)
                    {
                        _dosingSystemHelperLst[i].StopDosing();
                        _currentDosingOperation[i] = DosingOperation.None;
                    }
                }
                DosingEnterInit();
            }
            else
            {
                ReservoirEntity reservoirEntity = Singleton.Instance.GetModule(Module);
                if (_persistentValue != null && reservoirEntity != null && _persistentValue.OperatingMode != currentOperation)
                {
                    string preOperation = _persistentValue.OperatingMode;
                    if (reservoirEntity.IsBusy)
                    {
                        LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} is Busy, can't switch to Disabled mode");
                        return false;
                    }
                    if (_persistentValue.OperatingMode == "Auto" && reservoirEntity.IsMetalBusy)
                    {
                        string busymodule = "";
                        if (_metalDevices != null)
                        {
                            foreach (var item in _metalDevices)
                            {
                                MetalEntity metalEntity = Singleton.Instance.GetModule(item.Module.ToString());
                                if (metalEntity != null && metalEntity.IsBusy)
                                {
                                    busymodule += metalEntity.Module.ToString() + "/";
                                }
                            }
                        }
                        LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{busymodule} is Busy, can't switch to Disabled mode");
                        return false;
                    }
                    foreach (var metalDevice in _metalDevices)
                    {
                        metalDevice.DisabledOperation("", null);
                        MetalEntity metalEntity = Singleton.Instance.GetModule(metalDevice.Module);
                        metalEntity.AbortRecipe(null);
                        metalDevice.EnterDisabledOperation();
                    }
                    EnterDisabledOperation();
                    reservoirEntity.EnterInit();
                    _persistentValue.OperatingMode = currentOperation;
                    LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"Operating mode is switched from {preOperation} to {currentOperation}");
                }
                ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
                _currentOperation = ReservoirOperation.None;
            }
            return true;
        }
        /// 
        /// ManualAction
        /// 
        /// 
        /// 
        /// 
        private bool ManualOperation(string cmd, object[] args)
        {
            string currentOperation = "Manual";
            if (args.Length >= 1 && (bool)args[0])
            {
                foreach (var replenData in ReplenDatas)
                {
                    if (_replenPersistentValue != null && _replenPersistentValue[replenData.ReplenName] != null)
                    {
                        _replenPersistentValue[replenData.ReplenName].OperatingMode = currentOperation;
                        ReplenPersistentManager.Instance.UpdatePersistentValue(Module, replenData.ReplenName);
                    }
                }
                DosingEnterInit();
            }
            else
            {
                ReservoirEntity reservoirEntity = Singleton.Instance.GetModule(Module);
                if (_persistentValue != null && reservoirEntity != null && _persistentValue.OperatingMode != currentOperation)
                {
                    string preOperation = _persistentValue.OperatingMode;
                    if (reservoirEntity.IsBusy)
                    {
                        LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} is Busy, can't switch to Manual mode");
                        return false;
                    }
                    if (_persistentValue.OperatingMode == "Auto" && reservoirEntity.IsMetalBusy)
                    {
                        string busymodule = "";
                        if (_metalDevices != null)
                        {
                            foreach (var item in _metalDevices)
                            {
                                MetalEntity metalEntity = Singleton.Instance.GetModule(item.Module.ToString());
                                if (metalEntity != null && metalEntity.IsBusy)
                                {
                                    busymodule += metalEntity.Module.ToString() + "/";
                                }
                            }
                        }
                        LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{busymodule} is Busy, can't switch to Manual mode");
                        return false;
                    }
                    foreach (var metalDevice in _metalDevices)
                    {
                        metalDevice.ManualOperation("", null);
                    }
                    reservoirEntity.EnterInit();
                    if (_reservoirData.DiReplen) DIReplenOff("", null);
                    _persistentValue.OperatingMode = currentOperation;
                    LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"Operating mode is switched from {preOperation} to {currentOperation}");
                }
                ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
                _currentOperation = ReservoirOperation.None;
            }
            return true;
        }
        /// 
        /// AutoAction
        /// 
        /// 
        /// 
        /// 
        private bool AutoOperation(string cmd, object[] args)
        {
            double lowLevel = SC.GetValue($"Reservoir.{Module}.LowLevel");
            if (ReservoirData.WaterLevel < lowLevel)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Lowlevel was activated, can't switch to Auto mode");
                return false;
            }
            string currentOperation = "Auto";
            if (args.Length >= 1 && (bool)args[0])
            {
                foreach (var replenData in ReplenDatas)
                {
                    if (_replenPersistentValue != null && _replenPersistentValue[replenData.ReplenName] != null)
                    {
                        _replenPersistentValue[replenData.ReplenName].OperatingMode = currentOperation;
                        ReplenPersistentManager.Instance.UpdatePersistentValue(Module, replenData.ReplenName);
                    }
                }
                DosingEnterInit();
            }
            else
            {
                ReservoirEntity reservoirEntity = Singleton.Instance.GetModule(Module);
                if (_persistentValue != null && reservoirEntity != null && _persistentValue.OperatingMode != currentOperation)
                {
                    string preOperation = _persistentValue.OperatingMode;
                    if (reservoirEntity.IsBusy)
                    {
                        LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} is Busy, can't switch to Auto mode");
                        return false;
                    }
                    reservoirEntity.EnterInit();
                    _persistentValue.OperatingMode = currentOperation;
                    if (_reservoirData.DiReplen) DIReplenOff("", null);
                    //_isDiReplenInFault = false;
                    LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"Operating mode is switched from {preOperation} to {currentOperation}");
                }
                ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
            }
            return true;
        }
        /// 
        /// EngineeringModeAction
        /// 
        /// 
        /// 
        /// 
        private bool EngineeringModeOperation(string cmd, object[] args)
        {
            string currentRecipeOperation = "Engineering";
            if (_persistentValue != null)
            {
                _persistentValue.RecipeOperatingMode = currentRecipeOperation;
            }
            ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
            return true;
        }
        /// 
        /// ProductionAction
        /// 
        /// 
        /// 
        /// 
        private bool ProductionModeOperation(string cmd, object[] args)
        {
            string currentRecipeOperation = "Production";
            if (_persistentValue != null)
            {
                _persistentValue.RecipeOperatingMode = currentRecipeOperation;
            }
            ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
            return true;
        }
        /// 
        /// 加载Recipe
        /// 
        /// 
        /// 
        /// 
        private bool LoadRecipeOperation(string cmd, object[] args)
        {
            _persistentValue.Recipe = args[0].ToString();
            string[] fileRoute = _persistentValue.Recipe.Split('\\');
            string recipeRoute = "";
            if (fileRoute.Length > 2)
            {
                recipeRoute = fileRoute[fileRoute.Length - 2];
            }
            try
            {
                _resRecipe = RecipeFileManager.Instance.LoadGenericityRecipe(_persistentValue.Recipe);
                ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module.ToString());
                LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"Load {recipeRoute} Recipe {_resRecipe.Ppid} Success");
            }
            catch
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Load {recipeRoute} Recipe {_persistentValue.Recipe} failed");
            }
            return true;
        }
        /// 
        /// DiReplen On
        /// 
        /// 
        /// 
        /// 
        public bool DIReplenOnOperation(string cmd, object[] args)
        {
            return DIReplenOn();
        }
        /// 
        /// 自动注水
        /// 
        /// 
        public bool DIReplenOn()
        {
            ReservoirEntity entity = Singleton.Instance.GetModule(Module);
            if (ReservoirData.WaterLevel > SC.GetValue($"Reservoir.{Module}.HighLevel"))
            {
                if(entity!=null && !entity.IsError)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, "Highlevel is activate");
                }
                return false;
            }
            if(ReservoirData.WaterLevel < SC.GetValue($"Reservoir.{Module}.LowLevel")) 
            {
                if (entity != null && !entity.IsError)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"WaterLevel low is Activate");
                }
                return false;
            }
            bool preCondition = CheckPreDiReplenCondition();
            if (!preCondition)
            {
                return false;
            }
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{DI_REPLEN}");
            bool result = IOModuleManager.Instance.WriteIoValue(ioName, true);
            if (result)
            {
                _persistentValue.IsDiReplenOn = true;
            }
            return result;
        }
        /// 
        /// 打开继电器
        /// 
        /// 
        public void ResPowerOn()
        {
            bool result = false;
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{RESPOWERON}");
            if (!string.IsNullOrEmpty(ioName))
            {
                result = IOModuleManager.Instance.WriteIoValue(ioName, true);
                if (!result)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Open {Module} Power Relay failed");
                }
            }
            ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{HEDPOWERON}");
            if (!string.IsNullOrEmpty(ioName))
            {
                result = IOModuleManager.Instance.WriteIoValue(ioName, true);
                if (!result)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Open {Module} Hed Power Relay failed");
                }
            }
        }
        /// 
        /// 检验DiReplen前置条件
        /// 
        /// 
        public bool CheckPreDiReplenCondition()
        {
            if (!CheckFacilitiesDiReplenStatus())
            {
                if (!_isTotalDIReplenWarnOn)
                {
                    _isTotalDIReplenWarnOn = true;
                    LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, "Facilities DiReplen is Off");
                }
                return false;
            }
            else
            {
                _isTotalDIReplenWarnOn = false;
            }
            SafetyDevice safetyDevice = DEVICE.GetDevice("Safety");
            if (safetyDevice != null && safetyDevice.SafetyData.ReservoirHighLevel)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Safety high is Activate");
                return false;
            }
            if (CheckOtherReservoirDiReplenStatus())
            {
                return false;
            }
            return true;
        }
        /// 
        /// 检验总Di有没有开
        /// 
        /// 
        private bool CheckFacilitiesDiReplenStatus()
        {
            SystemFacilities systemFacilities = DEVICE.GetDevice("System.Facilities");
            if (systemFacilities != null)
            {
                return systemFacilities.DIReplenEnable;
            }
            return false;
        }
        /// 
        /// 检验是否其他Reservoir Direplen已经
        /// 
        /// 
        private bool CheckOtherReservoirDiReplenStatus()
        {
            List reservoirs = ReservoirItemManager.Instance.InstalledModules;
            foreach (string item in reservoirs)
            {
                if (item != Module)
                {
                    ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(item);
                    if (reservoirItem.SubType == STRATUS)
                    {
                        StandardHotReservoirDevice tmpDevice = DEVICE.GetDevice(item);
                        if (tmpDevice.ReservoirData.DiReplen)
                        {
                            if (!_isOtherDIReplenWarnOn)
                            {
                                _isOtherDIReplenWarnOn = true;
                                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{item} DIReplen is on");
                            }
                            return true;
                        }
                    }
                    else
                    {
                        CompactMembranReservoirDevice tmpDevice = DEVICE.GetDevice(item);
                        if (tmpDevice.ReservoirData.ANDiReplen)
                        {
                            if (!_isOtherDIReplenWarnOn)
                            {
                                _isOtherDIReplenWarnOn = true;
                                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{item} AN_DIReplen is on");
                            }
                            return true;
                        }
                        if (tmpDevice.ReservoirData.CADiReplen)
                        {
                            if (!_isOtherDIReplenWarnOn)
                            {
                                _isOtherDIReplenWarnOn = true;
                                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{item} CA_DIReplen is on");
                            }
                            return true;
                        }
                    }
                }
            }
            _isOtherDIReplenWarnOn = false;
            return false;
        }
        /// 
        /// DiReplen Off
        /// 
        /// 
        /// 
        /// 
        private bool DIReplenOff(string cmd, object[] args)
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{DI_REPLEN}");
            bool result = IOModuleManager.Instance.WriteIoValue(ioName, false);
            if (result)
            {
                _persistentValue.IsDiReplenOn = false;
                if (_currentOperation == ReservoirOperation.ManualDiReplen || _currentOperation == ReservoirOperation.AutoDiReplen)
                {
                    _currentOperation = ReservoirOperation.None;
                    _persistentValue.LastTotalReplen = _persistentValue.TotalReplen;
                    ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
                }
            }
            return result;
        }
        /// 
        /// 手动注水
        /// 
        /// 
        /// 
        /// 
        private bool ManualDiReplen(string cmd, object[] args)
        {
            if (_currentOperation != ReservoirOperation.None)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"current operation is {_currentOperation},cannot execute ManualDireplen operation");
                return false;
            }
            ReservoirEntity reservoirEntity = Singleton.Instance.GetModule(Module);
            if (!reservoirEntity.IsInitialized)
            {
                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{Module} is not initialized. Can't start DiReplen");
                return false;
            }
            if (DiReplenMaxTimeOut)
            {
                double diValveMaxOnTime = SC.GetValue($"Reservoir.{Module}.DIValveMaxOnTime");
                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"Direplen time over conifg's DIValveMaxOnTime:{diValveMaxOnTime} min");
                return false;
            }
            if (IsDiReplenInFault)
            {
                double diValveMaxOnTimePerFill = SC.GetValue($"Reservoir.{Module}.DIValveMaxOnTimePerFill");
                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"Direplen time over conifg's DIValveMaxOnTimePerFill:{diValveMaxOnTimePerFill} min");
                return false;
            }
            bool result = DIReplenOnOperation("", null);
            if (result)
            {
                _currentOperation = ReservoirOperation.ManualDiReplen;
                _persistentValue.DiReplenTime = DateTime.Now;
                ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
                int.TryParse(args[0].ToString(), out _manualReplenSecond);
            }
            return result;
        }
        /// 
        /// 自动注水
        /// 
        /// 
        public bool AutoDireplen()
        {
            if (ReservoirData.WaterLevel < SC.GetValue($"Reservoir.{Module}.LowLevel") && !_isAutoDIReplenError)
            {
                _isAutoDIReplenError = true;
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"LowLevel is activate,Can't AutoDireplen");
                return false;
            }
            else
            {
                _isAutoDIReplenError = false;
            }
            if (_currentOperation != ReservoirOperation.None)
            {
                return false;
            }
            if (_resRecipe == null)
            {
                return false;
            }
            bool result = DIReplenOn();
            if (result)
            {
                LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"{Module} CADIReplen State :True,  " +
                $"SetPoint = {Recipe.ReservoirCALevel}  " +
                $"Level = {_reservoirData.Level}  " +
                $"HighLimit = {Recipe.CALevelErrorHigh}  " +
                $"LowLimit = {Recipe.CALevelErrorLow}  ");
                _currentOperation = ReservoirOperation.AutoDiReplen;
                _persistentValue.DiReplenTime = DateTime.Now;
            }
            return result;
        }
        /// 
        /// 启动PH检测
        /// 
        public bool StartDetectPHValve()
        {
            string phProbeType = "";
            ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(Module.ToString());
            if (reservoirItem != null)
            {
                phProbeType = reservoirItem.PHProbeType;
            }
            if (_persistentValue.OperatingMode == MANUAL || string.IsNullOrEmpty(phProbeType))
            {
                return false;
            }
            _phState = _phRoutine.Start();
            LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, "Start Detect PH");
            return true;
        }
        /// 
        /// 打开PH Valve
        /// 
        /// 
        public bool PHValveOn()
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{PH_FLOW_VALVE}");
            return IOModuleManager.Instance.WriteIoValue(ioName, true);
        }
        /// 
        /// 关闭PH Valve
        /// 
        /// 
        public bool PHValveOff()
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{PH_FLOW_VALVE}");
            return IOModuleManager.Instance.WriteIoValue(ioName, false);
        }
        /// 
        /// CAFlowRateCheck
        /// 
        private void CAFlowRateCheck()
        {
            if (_resRecipe == null) return;
            for (int i = 0; i < _metalDevices.Count; i++)
            {
                StandardHotMetalDevice hotMetalDevice = _metalDevices[i];
                if (hotMetalDevice != null && !hotMetalDevice.IsDisable && hotMetalDevice.IsAuto)
                {
                    if (hotMetalDevice.MetalDeviceData == null || !hotMetalDevice.MetalDeviceData.Circulation) continue;
                    if (!hotMetalDevice.FlowValveStable) continue;
                    MetalEntity metalEntity = Singleton.Instance.GetModule(hotMetalDevice.Name);
                    double cellFlow = hotMetalDevice.MetalDeviceData.CellFlow;
                    //if (cellFlow < _resRecipe.CAFlowRateErrorLow)
                    //{
                    //    if (!metalEntity.IsError)
                    //    {
                    //        LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"cell {hotMetalDevice.Name} cellflow:{cellFlow} is less than recipe's CAFlowRateErrorLow parameter:{_resRecipe.CAFlowRateErrorLow}");
                    //        metalEntity.PostMsg(MetalMsg.Error);
                    //    }
                    //}
                    bool cellFlowSignalresulte = _holdoffTimeSignalDic[$"{hotMetalDevice.Name}_CellFlow"].IsSignalAbnormal(_cellFlowHoldoffTime, cellFlow, null, _resRecipe.CAFlowRateErrorLow);
                    if (cellFlowSignalresulte)
                    {
                        if (!metalEntity.IsError)
                        {
                            LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{hotMetalDevice.Name} cellflow:{cellFlow} is less than recipe's CAFlowRateErrorLow parameter:{_resRecipe.CAFlowRateErrorLow}");
                            metalEntity.PostMsg(MetalMsg.Error);
                        }
                    }
                    else if (cellFlow < _resRecipe.CAFlowRateWarningLow)
                    {
                        if (!_isCAFlowRateWARN[i])
                        {
                            LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"cell {hotMetalDevice.Name} cellflow:{cellFlow} is less than recipe's CAFlowRateWarningLow parameter:{_resRecipe.CAFlowRateWarningLow}");
                            _isCAFlowRateWARN[i] = true;
                        }
                    }
                    else
                    {
                        _isCAFlowRateWARN[i] = false;
                    }
                }
            }
        }
        /// 
        /// Temperature Check
        /// 
        private void TemperatureCheck()
        {
            ReservoirEntity reservoirEntity = Singleton.Instance.GetModule(Module);
            ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(Module);
            TemperatureController temperatureController = DEVICE.GetDevice(reservoirItem.TCID);
            if (temperatureController == null || temperatureController.TemperatureData == null || _resRecipe == null
                || temperatureController.TemperatureData.ControlOperationModel != ENABLE) return;
            double tempValue = temperatureController.TemperatureData.ReserviorTemperature;
            if (tempValue > _resRecipe.TemperatureErrorHigh && !reservoirEntity.IsError)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{temperatureController.Name} temperature:{tempValue} is over recipe's TemperatureErrorHigh parameter:{_resRecipe.TemperatureErrorHigh}");
                reservoirEntity.PostMsg(ReservoirMsg.Error);
            }
            else if (tempValue < _resRecipe.TemperatureErrorLow && !reservoirEntity.IsError)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{temperatureController.Name} temperature:{tempValue} is less than recipe's TemperatureErrorLow parameter:{_resRecipe.TemperatureErrorLow}");
                reservoirEntity.PostMsg(ReservoirMsg.Error);
            }
            else if (tempValue > _resRecipe.TemperatureWarningHigh)
            {
                if (!_isTCControlWARN)
                {
                    LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{temperatureController.Name} temperature:{tempValue} is over recipe's TemperatureWarningHigh parameter:{_resRecipe.TemperatureWarningHigh}");
                    _isTCControlWARN = true;
                }
            }
            else if (tempValue < _resRecipe.TemperatureWarningLow)
            {
                if (!_isTCControlWARN)
                {
                    LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{temperatureController.Name} temperature:{tempValue} is less than recipe's TemperatureWarningLow parameter:{_resRecipe.TemperatureWarningLow}");
                    _isTCControlWARN = true;
                }
            }
            else
            {
                _isTCControlWARN = false;
            }
        }
        /// 
        /// CMMPowerCheck
        /// 
        private void CMMPowerCheck()
        {
            ReservoirEntity reservoirEntity = Singleton.Instance.GetModule(Module);
            ReservoirItem _reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(Module.ToString());
            if (!string.IsNullOrEmpty(_reservoirItem.CMMSupplyID))
            {
                CellPowerSupplier _powerSupplier = DEVICE.GetDevice(_reservoirItem.CMMSupplyID);
                if (_powerSupplier == null || _powerSupplier.PowerSupplierData == null || _resRecipe == null || !_resRecipe.CMMEnable || !_powerSupplier.PowerSupplierData.Enabled || _powerSupplier.PowerSupplierData.Current == 0) return;
                //CMM Current Check
                double current = _powerSupplier.PowerSupplierData.Current;
                if (current > _resRecipe.CMMCurrentSetPoint * (1 + (double)_resRecipe.CMMCurrentFaultPercent / 100))
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{_powerSupplier.Name} current:{current} is over error upper limit:{_resRecipe.CMMCurrentSetPoint * (1 + (double)_resRecipe.CMMCurrentFaultPercent / 100)} based on recipe's CMMCurrentFaultPercent parameter:{_resRecipe.CMMCurrentFaultPercent} %");
                    reservoirEntity.PostMsg(ReservoirMsg.Error);
                    if (_powerSupplier.PowerSupplierData.Enabled)
                    {
                        _powerSupplier.DisableOperation("", null);
                        _persistentValue.CMMStartTime = DateTime.MinValue;
                        ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
                    }
                }
                else if (current < _resRecipe.CMMCurrentSetPoint * (1 - (double)_resRecipe.CMMCurrentFaultPercent / 100))
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{_powerSupplier.Name} current:{current} is less than error lower limit:{_resRecipe.CMMCurrentSetPoint * (1 - (double)_resRecipe.CMMCurrentFaultPercent / 100)} based on recipe's CMMCurrentFaultPercent parameter:{_resRecipe.CMMCurrentFaultPercent}%");
                    reservoirEntity.PostMsg(ReservoirMsg.Error);
                    if (_powerSupplier.PowerSupplierData.Enabled)
                    {
                        _powerSupplier.DisableOperation("", null);
                        _persistentValue.CMMStartTime = DateTime.MinValue;
                        ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
                    }
                }
                else if (current > _resRecipe.CMMCurrentSetPoint * (1 + (double)_resRecipe.CMMCurrentWarningPercent / 100))
                {
                    if (!_isCMMPowerCurrentWARN)
                    {
                        LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{_powerSupplier.Name} current:{current} is over warning upper limit:{_resRecipe.CMMCurrentSetPoint * (1 + (double)_resRecipe.CMMCurrentWarningPercent / 100)} based on recipe's CMMCurrentWarningPercent parameter:{_resRecipe.CMMCurrentWarningPercent}%");
                        _isCMMPowerCurrentWARN = true;
                    }
                }
                else if (current < _resRecipe.CMMCurrentSetPoint * (1 - (double)_resRecipe.CMMCurrentWarningPercent / 100))
                {
                    if (!_isCMMPowerCurrentWARN)
                    {
                        LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{_powerSupplier.Name} current:{current} is less than warning lower limit{_resRecipe.CMMCurrentSetPoint * (1 - (double)_resRecipe.CMMCurrentWarningPercent / 100)} based on recipe's CMMCurrentWarningPercent parameter:{_resRecipe.CMMCurrentWarningPercent}%");
                        _isCMMPowerCurrentWARN = true;
                    }
                }
                else
                {
                    _isCMMPowerCurrentWARN = false;
                }
                //CMM Voltage Check
                double voltage = _powerSupplier.PowerSupplierData.Voltage;
                if (voltage < _resRecipe.CMMMinVoltage)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{_powerSupplier.Name} voltage:{voltage} is less than recipe's CMMMinVoltage parameter:{_resRecipe.CMMMinVoltage}");
                    reservoirEntity.PostMsg(ReservoirMsg.Error);
                    if (_powerSupplier.PowerSupplierData.Enabled) _powerSupplier.DisableOperation("", null);
                }
                //CMM Flow Check
                double flow = ReservoirData.Flow;
                double cmmFlowHighFault = SC.GetValue($"Reservoir.{Module}.CMMFlowHighFault");
                double cmmFlowHighWarning = SC.GetValue($"Reservoir.{Module}.CMMFlowHighWarning");
                double cmmFlowLowFault = SC.GetValue($"Reservoir.{Module}.CMMFlowLowFault");
                double cmmFlowLowWarning = SC.GetValue($"Reservoir.{Module}.CMMFlowLowWarning");
                if (flow < cmmFlowLowFault)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} CMM flow:{flow} is less than config item CMMFlowLowFault:{cmmFlowLowFault}");
                    if (_powerSupplier.PowerSupplierData.Enabled) _powerSupplier.DisableOperation("", null);
                    reservoirEntity.PostMsg(ReservoirMsg.Error);
                }
                else if (flow < cmmFlowLowWarning)
                {
                    if (!_isCMMPowerFlowWARN)
                    {
                        LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{Module} CMM flow:{flow} is less than config item CMMFlowLowWarning:{cmmFlowLowWarning}");
                        _isCMMPowerFlowWARN = true;
                    }
                }
                else if (flow > cmmFlowHighFault)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} CMM flow:{flow} is over config item CMMFlowHighFault:{cmmFlowLowFault}");
                    if (_powerSupplier.PowerSupplierData.Enabled) _powerSupplier.DisableOperation("", null);
                    reservoirEntity.PostMsg(ReservoirMsg.Error);
                }
                else if (flow > cmmFlowHighWarning)
                {
                    if (!_isCMMPowerFlowWARN)
                    {
                        LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{Module} CMM flow:{flow} is over config item CMMFlowHighWarning:{cmmFlowLowWarning}");
                        _isCMMPowerFlowWARN = true;
                    }
                }
                else
                {
                    _isCMMPowerFlowWARN = false;
                }
            }
        }
        #region DosingSystem
        /// 
        /// 加载Replen Recipe
        /// 
        /// 
        /// 
        /// 
        private bool LoadDosingRecipeOperation(string cmd, object[] args)
        {
            string replenName = args[1].ToString();
            int replenID = int.Parse(args[1].ToString().Substring(6, 1));
            string replenRecipe = args[0].ToString();
            _rdsRecipe[replenID - 1] = RecipeFileManager.Instance.LoadGenericityRecipe(replenRecipe);
            _replenDatas[replenID - 1].RecipeName = args[2].ToString();
            LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"Load Replen Recipe {_rdsRecipe[replenID - 1].Ppid} Success");
            return true;
        }
        #region Replen Pump
        /// 
        /// Replen Pump On 操作(根据PumpFactor和InitialDosingSpeed调速)
        /// 
        /// 
        /// 
        /// 
        public bool ReplenPumpOnOperation(string cmd, object[] args)
        {
            string replenName = args[0].ToString();
            int replenID = int.Parse(args[0].ToString().Substring(6, 1));
            //加载InitialDosingSpeed
            double InitialDosingSpeed = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.InitialDosingSpeed"))
            {
                InitialDosingSpeed = SC.GetValue($"Reservoir.{Module}.InitialDosingSpeed");
                if (InitialDosingSpeed == 0)
                {
                    _replenDatas[replenID - 1].ReplenPumpEnable = false;
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, "InitialDosingSpeed is zero. Can't open replen pump");
                    return false;
                }
            }
            else
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, "Configuration item of InitialDosingSpeed doesn't exist!");
                return false;
            }
            //加载ReplenPumpFactor
            double PumpFactor = 0;
            PumpFactor = _replenPersistentValue[replenName].ReplenPumpFactor;
            if (PumpFactor == 0)
            {
                _replenDatas[replenID - 1].ReplenPumpEnable = false;
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, "PumpFactor is zero. Can't open replen pump");
                return false;
            }
            //计算PumpSpeed(mL/min)
            double replenPumpSpeed = InitialDosingSpeed * PumpFactor;
            SCConfigItem item = SC.GetConfigItem($"Reservoir.{Module}.InitialDosingSpeed");
            double speedMax = double.Parse(item.Max);
            if (replenPumpSpeed > speedMax) replenPumpSpeed = speedMax;
            bool result = ReplenPump(replenPumpSpeed / PUMP_SPEED_CONVERT, replenID);
            if (!result)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Opening replen{replenID} pump is failed");
                _replenDatas[replenID - 1].ReplenPumpSpeed = 0;
                _replenDatas[replenID - 1].ReplenPumpEnable = false;
                return false;
            }
            _replenDatas[replenID - 1].ReplenPumpSpeed = replenPumpSpeed;
            _replenDatas[replenID - 1].ReplenPumpEnable = true;
            LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"Replen{replenID} pump is opened");
            return true;
        }
        /// 
        /// Replen Pump Off操作
        /// 
        /// 
        /// 
        /// 
        public bool ReplenPumpOffOperation(string cmd, object[] args)
        {
            string replenName = args[0].ToString();
            int replenID = int.Parse(args[0].ToString().Substring(6, 1));
            bool result = ReplenPump(0, replenID);
            if (!result)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Closing replen{replenID} pump is failed");
                return false;
            }
            _replenDatas[replenID - 1].ReplenPumpSpeed = 0;
            _replenDatas[replenID - 1].ReplenPumpEnable = false;
            LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"Replen{replenID} pump is closed");
            return true;
        }
        /// 
        /// Replen Pump WriteIOValue
        /// 
        /// 
        /// 
        private bool ReplenPump(double speed, int replenNum)
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.Replen{replenNum}PumpSpeed");
            return IOModuleManager.Instance.WriteIoValue(ioName, speed);
        }
        #endregion
        /// 
        /// 设置PumpFactor
        /// 
        /// 
        /// 
        /// 
        public bool SetPumpFactor(string cmd, object[] args)
        {
            int replenID = int.Parse(args[0].ToString().Substring(6, 1));
            double targetPumpFactor = (double)args[1];
            _dosingSystemHelperLst[replenID - 1].SetPumpfactor(targetPumpFactor);
            return true;
        }
        /// 
        /// 手动Dosing
        /// 
        /// 
        /// 
        /// 
        private bool ManualDosing(string cmd, object[] args)
        {
            ReservoirEntity reservoirEntity = Singleton.Instance.GetModule(Module);
            if (reservoirEntity == null || !_dosingCommonHelper.IsDosingSystemInitialized)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, "Dosing System is not initialized!");
                return true;
            }
            string replenName = args[0].ToString();
            int replenID = int.Parse(args[0].ToString().Substring(6, 1));
            double manualDosingVolume = (double)args[1];
            if (manualDosingVolume == 0)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, "Dosing Volume is zero, can't do manual dosing");
                return false;
            }
            if (!CheckandUpdateBottleLevel(replenName, manualDosingVolume))
            {
                return false;
            }
            if (_replenPersistentValue[replenName].IsDosingRunning)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, "Dosing is runnning, can't do manual dosing");
                return false;
            }
            LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, "ManualDosing starts now");
            _currentDosingOperation[replenID - 1] = DosingOperation.ManualDosing;
            bool result = _dosingSystemHelperLst[replenID - 1].StartDosing(manualDosingVolume, false);
            return result;
        }
        /// 
        /// 停止手动Dosing
        /// 
        /// 
        /// 
        /// 
        private bool StopManualDosing(string cmd, object[] args)
        {
            string replenName = args[0].ToString();
            int replenID = int.Parse(args[0].ToString().Substring(6, 1));
            if (!_replenPersistentValue[replenName].IsDosingRunning)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, "ManualDosing is not running. Can't stop!");
            }
            _currentDosingOperation[replenID - 1] = DosingOperation.None;
            bool result = _dosingSystemHelperLst[replenID - 1].StopDosing();
            if (result)
            {
                LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, "ManualDosing has stopped now");
            }
            return result;
        }
        /// 
        /// 检查并更新BottleLevel状态
        /// 
        /// 
        /// 
        public bool CheckandUpdateBottleLevel(string replenName, double targetVolume = -1, bool isError = true)
        {
            int replenId = int.Parse(replenName.Substring(6, 1));
            double remainVolume = _replenPersistentValue[replenName].RemainDosingVolume;
            bool result = true;
            if (ReservoirData.ReplenLevel[replenId - 1])
            {
                ReplenDatas[replenId - 1].BottleLevel = DosingSystemHelper.BottleLevelState.Full.ToString();
                if (targetVolume > remainVolume)
                {
                    ReplenDatas[replenId - 1].BottleLevel = DosingSystemHelper.BottleLevelState.Warning.ToString();
                    if (isError)
                    {
                        LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Current Bottle is in Warning Level!");
                    }
                    result = false;
                }
            }
            else
            {
                ReplenDatas[replenId - 1].BottleLevel = DosingSystemHelper.BottleLevelState.Empty.ToString();
                if (isError)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Current Bottle is in Empty Level!");
                }
                result = false;
            }
            return result;
        }
        /// 
        /// Reset BottleVolume
        /// 
        /// 
        /// 
        /// 
        private bool ResetBottleVolume(string cmd, object[] args)
        {
            string replenName = args[0].ToString();
            int replenID = int.Parse(args[0].ToString().Substring(6, 1));
            if (SC.ContainsItem($"Reservoir.{Module}.BottleReserveVolume{replenID}"))
            {
                _replenPersistentValue[replenName].RemainDosingVolume = SC.GetValue($"Reservoir.{Module}.BottleReserveVolume{replenID}");
            }
            ReplenPersistentManager.Instance.UpdatePersistentValue(Module, replenName);
            LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"Bottle Reserve Volume of Replen{replenID} has been reset now");
            return true;
        }
        /// 
        /// DosingSystem初始化
        /// 
        /// 
        /// 
        /// 
        private bool DosingInitialize(string cmd, object[] args)
        {
            ReservoirEntity reservoirEntity = Singleton.Instance.GetModule(Module);
            if (reservoirEntity == null || !reservoirEntity.IsInitialized)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} is not initialized");
                return false;
            }
            return _dosingCommonHelper.DosingInitialize(); ;
        }
        /// 
        /// Enetr Init State
        /// 
        /// 
        /// 
        /// 
        private bool DosingEnterInit()
        {
            return _dosingCommonHelper.DosingEnterInit();
        }
        #endregion
        /// 
        /// Enter Disabled Operation
        /// 
        private void EnterDisabledOperation()
        {
            ReservoirItem _reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(Module.ToString());
            //关CMMPower
            if (!string.IsNullOrEmpty(_reservoirItem.CMMSupplyID))
            {
                CellPowerSupplier powerSupplier = DEVICE.GetDevice(_reservoirItem.CMMSupplyID);
                if (powerSupplier != null && powerSupplier.PowerSupplierData.Enabled) powerSupplier.DisableOperation("", null);
            }
            //关TC
            if (!string.IsNullOrEmpty(_reservoirItem.TCID))
            {
                TemperatureController temperatureController = DEVICE.GetDevice(_reservoirItem.TCID);
                if (temperatureController != null) temperatureController.DisableOperation("", null);
            }
            _phRoutine.Abort();
            if (_reservoirData.PHFlowValve)
            {
                PHValveOff();
            }
            if (_reservoirData.DiReplen)
            {
                DIReplenOff("", null);
            }
        }
        /// 
        /// Set Export CMMUsage
        /// 
        public void SetExportCMMUsage()
        {
            _isExportCMMUsage = true;
        }
        /// 
        /// ReservoirUsage监控
        /// 
        public void ReservoirUsageMonitor()
        {
            ReservoirUsage reservoirUsage = ReservoirUsageManager.Instance.GetReservoirUsage(Module);
            ReservoirEntity reservoirEntity = Singleton.Instance.GetModule(Module);
            if (reservoirUsage == null || reservoirEntity == null) return;
            //reservoirTotalAmpHours Check
            double reservoirTotalAmpHoursWarningLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.ReservoirTotalAmpHoursWarningLimit"))
            {
                reservoirTotalAmpHoursWarningLimit = (double)SC.GetValue($"Reservoir.{Module}.ReservoirTotalAmpHoursWarningLimit");
            }
            double reservoirTotalAmpHoursFaultLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.ReservoirTotalAmpHoursFaultLimit"))
            {
                reservoirTotalAmpHoursFaultLimit = (double)SC.GetValue($"Reservoir.{Module}.ReservoirTotalAmpHoursFaultLimit");
            }
            if (reservoirUsage.TotalUsage > reservoirTotalAmpHoursFaultLimit && reservoirTotalAmpHoursFaultLimit != 0)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} Total Usage(AHr):{reservoirUsage.TotalUsage} is over config item ReservoirTotalAmpHoursFaultLimit:{reservoirTotalAmpHoursFaultLimit}");
                reservoirEntity.PostMsg(ReservoirMsg.Error);
                AlarmListManager.Instance.AddDataError(Module,
                    $"TotalUsage", $"{Module} usage:{reservoirUsage.TotalUsage} is over ReservoirTotalAmpHoursFaultLimit:{reservoirTotalAmpHoursFaultLimit}");
            }
            else if (reservoirUsage.TotalUsage > reservoirTotalAmpHoursWarningLimit && reservoirTotalAmpHoursWarningLimit != 0)
            {
                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{Module} Total Usage(AHr):{reservoirUsage.TotalUsage} is over config item ReservoirTotalAmpHoursWarningLimit:{reservoirTotalAmpHoursWarningLimit}");
                AlarmListManager.Instance.AddWarn(Module,
                    $"TotalUsage", $"{Module} usage:{reservoirUsage.TotalUsage} is over ReservoirTotalAmpHoursWarningLimit:{reservoirTotalAmpHoursWarningLimit}");
            }
            //MembraneTotalAmpHoursCheck
            double membraneTotalAmpHoursWarningLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.MembraneTotalAmpHoursWarningLimit"))
            {
                membraneTotalAmpHoursWarningLimit = (double)SC.GetValue($"Reservoir.{Module}.MembraneTotalAmpHoursWarningLimit");
            }
            double membraneTotalAmpHoursFaultLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.MembraneTotalAmpHoursFaultLimit"))
            {
                membraneTotalAmpHoursFaultLimit = (double)SC.GetValue($"Reservoir.{Module}.MembraneTotalAmpHoursFaultLimit");
            }
            if (reservoirUsage.MembranceUsage > membraneTotalAmpHoursFaultLimit && membraneTotalAmpHoursFaultLimit != 0)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} Membrane Usage(AHr):{reservoirUsage.MembranceUsage} is over config item MembraneTotalAmpHoursFaultLimit:{membraneTotalAmpHoursFaultLimit}");
                reservoirEntity.PostMsg(ReservoirMsg.Error);
                AlarmListManager.Instance.AddDataError(Module,
                    $"MembraneUsage", $"{Module} usage:{reservoirUsage.MembranceUsage} is over MembraneTotalAmpHoursFaultLimit:{membraneTotalAmpHoursFaultLimit}");
            }
            else if (reservoirUsage.MembranceUsage > membraneTotalAmpHoursWarningLimit && membraneTotalAmpHoursWarningLimit != 0)
            {
                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{Module} Membrane Usage(AHr):{reservoirUsage.MembranceUsage} is over config item MembraneTotalAmpHoursWarningLimit:{membraneTotalAmpHoursWarningLimit}");
                AlarmListManager.Instance.AddWarn(Module,
                   $"MembraneUsage", $"{Module} usage:{reservoirUsage.MembranceUsage} is over MembraneTotalAmpHoursWarningLimit:{membraneTotalAmpHoursWarningLimit}");
            }
            double bathTotalAmpHoursWarningLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.BathTotalAmpHoursWarningLimit"))
            {
                bathTotalAmpHoursWarningLimit = (double)SC.GetValue($"Reservoir.{Module}.BathTotalAmpHoursWarningLimit");
            }
            double bathTotalAmpHoursFaultLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.BathTotalAmpHoursFaultLimit"))
            {
                bathTotalAmpHoursFaultLimit = (double)SC.GetValue($"Reservoir.{Module}.BathTotalAmpHoursFaultLimit");
            }
            if (reservoirUsage.BathUsage > bathTotalAmpHoursFaultLimit && bathTotalAmpHoursFaultLimit != 0)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} Bath Usage(AHr):{reservoirUsage.BathUsage} is over config item BathTotalAmpHoursFaultLimit:{bathTotalAmpHoursFaultLimit}");
                reservoirEntity.PostMsg(ReservoirMsg.Error);
                AlarmListManager.Instance.AddDataError(Module,
                    $"BathUsage", $"{Module} usage:{reservoirUsage.BathUsage} is over BathTotalAmpHoursFaultLimit:{bathTotalAmpHoursFaultLimit}");
            }
            else if (reservoirUsage.BathUsage > bathTotalAmpHoursWarningLimit && bathTotalAmpHoursWarningLimit != 0)
            {
                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{Module} Bath Usage(AHr):{reservoirUsage.BathUsage} is over config item BathTotalAmpHoursWarningLimit:{bathTotalAmpHoursWarningLimit}");
                AlarmListManager.Instance.AddWarn(Module,
                    $"BathUsage", $"{Module} usage:{reservoirUsage.BathUsage} is over BathTotalAmpHoursWarningLimit:{bathTotalAmpHoursWarningLimit}");
            }
            //BathTotalDaysCheck
            int bathTotalDaysWarningLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.BathTotalDaysWarningLimit"))
            {
                bathTotalDaysWarningLimit = SC.GetValue($"Reservoir.{Module}.BathTotalDaysWarningLimit");
            }
            int bathTotalDaysFaultLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.BathTotalDaysFaultLimit"))
            {
                bathTotalDaysFaultLimit = SC.GetValue($"Reservoir.{Module}.BathTotalDaysFaultLimit");
            }
            if (reservoirUsage.BathUsageDays > bathTotalDaysFaultLimit && bathTotalDaysFaultLimit != 0)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} Bath Usage(Days):{reservoirUsage.BathUsageDays} is over config item BathTotalDaysFaultLimit:{bathTotalDaysFaultLimit}");
                reservoirEntity.PostMsg(ReservoirMsg.Error);
                AlarmListManager.Instance.AddDataError(Module,
                    $"BathUsageDays", $"{Module} usage:{reservoirUsage.BathUsageDays} is over BathTotalDaysFaultLimit:{bathTotalDaysFaultLimit}");
            }
            else if (reservoirUsage.BathUsageDays > bathTotalDaysWarningLimit && bathTotalDaysWarningLimit != 0)
            {
                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{Module} Bath Usage(Days):{reservoirUsage.BathUsageDays} is over config item BathTotalDaysWarningLimit:{bathTotalDaysWarningLimit}");
                AlarmListManager.Instance.AddWarn(Module,
                    $"BathUsageDays", $"{Module} usage:{reservoirUsage.BathUsageDays} is over BathTotalDaysWarningLimit:{bathTotalDaysWarningLimit}");
            }
            //ReservoirTotalWafersCheck
            int reservoirTotalWafersWarningLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.ReservoirTotalWafersWarningLimit"))
            {
                reservoirTotalWafersWarningLimit = SC.GetValue($"Reservoir.{Module}.ReservoirTotalWafersWarningLimit");
            }
            int reservoirTotalWafersFaultLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.ReservoirTotalWafersFaultLimit"))
            {
                reservoirTotalWafersFaultLimit = SC.GetValue($"Reservoir.{Module}.ReservoirTotalWafersFaultLimit");
            }
            if (reservoirUsage.TotalWafers > reservoirTotalWafersFaultLimit && reservoirTotalWafersFaultLimit != 0)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} Total Wafers:{reservoirUsage.TotalWafers} is over config item ReservoirTotalWafersFaultLimit:{reservoirTotalWafersFaultLimit}");
                reservoirEntity.PostMsg(ReservoirMsg.Error);
                AlarmListManager.Instance.AddDataError(Module,
                    $"TotalWafers", $"{Module} usage:{reservoirUsage.TotalWafers} is over ReservoirTotalWafersFaultLimit:{reservoirTotalWafersFaultLimit}");
            }
            else if (reservoirUsage.TotalWafers > reservoirTotalWafersWarningLimit && reservoirTotalWafersWarningLimit != 0)
            {
                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{Module} Total Wafers:{reservoirUsage.TotalWafers} is over config item ReservoirTotalWafersWarningLimit:{reservoirTotalWafersWarningLimit}");
                AlarmListManager.Instance.AddWarn(Module,
                    $"TotalWafers", $"{Module} usage:{reservoirUsage.TotalWafers} is over ReservoirTotalWafersWarningLimit:{reservoirTotalWafersWarningLimit}");
            }
            //CMMAnodeTotalAmpHoursCheck
            double cmmAnodeTotalAmpHoursWarningLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.CMMAnodeTotalAmpHoursWarningLimit"))
            {
                cmmAnodeTotalAmpHoursWarningLimit = (double)SC.GetValue($"Reservoir.{Module}.CMMAnodeTotalAmpHoursWarningLimit");
            }
            double cmmAnodeTotalAmpHoursFaultLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.CMMAnodeTotalAmpHoursFaultLimit"))
            {
                cmmAnodeTotalAmpHoursFaultLimit = (double)SC.GetValue($"Reservoir.{Module}.CMMAnodeTotalAmpHoursFaultLimit");
            }
            if (reservoirUsage.CMMAnodeUsage > cmmAnodeTotalAmpHoursFaultLimit && cmmAnodeTotalAmpHoursFaultLimit != 0)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} CMM Anode Usage(AHr):{reservoirUsage.CMMAnodeUsage} is over config item CMMAnodeTotalAmpHoursFaultLimit:{cmmAnodeTotalAmpHoursFaultLimit}");
                reservoirEntity.PostMsg(ReservoirMsg.Error);
                AlarmListManager.Instance.AddDataError(Module,
                    $"CMMAnodeUsage", $"{Module} CMM Anode Usage(AHr):{reservoirUsage.CMMAnodeUsage} is over CMMAnodeTotalAmpHoursFaultLimit:{cmmAnodeTotalAmpHoursFaultLimit}");
            }
            else if (reservoirUsage.CMMAnodeUsage > cmmAnodeTotalAmpHoursWarningLimit && cmmAnodeTotalAmpHoursWarningLimit != 0)
            {
                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{Module} CMM Anode Usage(AHr):{reservoirUsage.CMMAnodeUsage} is over config item CMMAnodeTotalAmpHoursWarningLimit:{cmmAnodeTotalAmpHoursWarningLimit}");
                AlarmListManager.Instance.AddWarn(Module,
                    $"CMMAnodeUsage", $"{Module} CMM Anode Usage(AHr):{reservoirUsage.CMMAnodeUsage} is over CMMAnodeTotalAmpHoursWarningLimit:{cmmAnodeTotalAmpHoursWarningLimit}");
            }
            //CMMCathodeTotalAmpHoursCheck
            double cmmCathodeTotalAmpHoursWarningLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.CMMCathodeTotalAmpHoursWarningLimit"))
            {
                cmmCathodeTotalAmpHoursWarningLimit = (double)SC.GetValue($"Reservoir.{Module}.CMMCathodeTotalAmpHoursWarningLimit");
            }
            double cmmCathodeTotalAmpHoursFaultLimit = 0;
            if (SC.ContainsItem($"Reservoir.{Module}.CMMCathodeTotalAmpHoursFaultLimit"))
            {
                cmmCathodeTotalAmpHoursFaultLimit = (double)SC.GetValue($"Reservoir.{Module}.CMMCathodeTotalAmpHoursFaultLimit");
            }
            if (reservoirUsage.CMMMembranceUsage > cmmCathodeTotalAmpHoursFaultLimit && cmmCathodeTotalAmpHoursFaultLimit != 0)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{Module} CMM Cathode Usage(AHr):{reservoirUsage.CMMMembranceUsage} is over config item CMMCathodeTotalAmpHoursFaultLimit:{cmmCathodeTotalAmpHoursFaultLimit}");
                reservoirEntity.PostMsg(ReservoirMsg.Error);
                AlarmListManager.Instance.AddDataError(Module,
                    $"CMMCathodeUsage", $"{Module} CMM Cathode Usage(AHr):{reservoirUsage.CMMMembranceUsage} is over CMMCathodeTotalAmpHoursFaultLimit:{cmmCathodeTotalAmpHoursFaultLimit}");
            }
            else if (reservoirUsage.CMMMembranceUsage > cmmCathodeTotalAmpHoursWarningLimit && cmmCathodeTotalAmpHoursWarningLimit != 0)
            {
                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{Module} CMM Cathode Usage(AHr):{reservoirUsage.CMMMembranceUsage} is over config item CMMCathodeTotalAmpHoursWarningLimit:{cmmCathodeTotalAmpHoursWarningLimit}");
                AlarmListManager.Instance.AddWarn(Module,
                    $"CMMCathodeUsage", $"{Module} CMM Cathode Usage(AHr):{reservoirUsage.CMMMembranceUsage} is over CMMCathodeTotalAmpHoursWarningLimit:{cmmCathodeTotalAmpHoursWarningLimit}");
            }
        }
        #endregion
        /// 
        /// 订阅变量数值发生变化
        /// 
        private void SubscribeValueAction()
        {
            BeckhoffIoSubscribeUpdateVariable(FLOW);
            BeckhoffIoSubscribeUpdateVariable(HED_FLOW);
            BeckhoffIoSubscribeUpdateVariable(DI_REPLEN);
            BeckhoffIoSubscribeUpdateVariable(PH_FLOW_VALVE);
            BeckhoffIoSubscribeUpdateVariable(PH_VALUE);
            BeckhoffIoSubscribeUpdateVariable(WATER_LEVEL);
            BeckhoffIoSubscribeUpdateVariable(LOW_LEVEL);
            BeckhoffIoSubscribeUpdateVariable(HIGH_LEVEL);
            BeckhoffIoSubscribeUpdateVariable(SAFETY_HIGH_LEVEL);
            BeckhoffIoSubscribeUpdateVariable(RESPOWERON);
            BeckhoffIoSubscribeUpdateVariable(HEDPOWERON);
            BeckhoffIoSubscribeUpdateVariable(REGULATE_PUMP_SIGNAL_IN);
            BeckhoffIoSubscribeUpdateVariable(REGULATE_PUMP_SPEED);
            for (int i = 0; i < _replenNum; i++)
            {
                BeckhoffIoSubscribeUpdateVariable($"Replen{i + 1}Level");
            }
        }
        /// 
        /// 订阅IO变量
        /// 
        /// 
        private void BeckhoffIoSubscribeUpdateVariable(string variable)
        {
            _variableInitializeDic[variable] = false;
            IOModuleManager.Instance.SubscribeModuleVariable(Module, variable, UpdateVariableValue);
        }
        /// 
        /// 更新变量数值
        /// 
        /// 
        /// 
        private void UpdateVariableValue(string variable, object value)
        {
            if (!ReservoirData.IsDataInitialized)
            {
                ReservoirData.IsDataInitialized = true;
            }
            PropertyInfo property = ReservoirData.GetType().GetProperty(variable);
            if (property != null)
            {
                property.SetValue(_reservoirData, value);
                if (variable == WATER_LEVEL)
                {
                    string levelCurve = SC.GetStringValue($"Reservoir.{Module}.LevelCurve");
                    ReservoirData.Level = LevelCurveManager.Instance.CalculateLevelByWaterLevel(ReservoirData.WaterLevel, levelCurve);
                }
            }
            if (variable.Contains("Replen") && variable.Substring(0, 6) == "Replen")
            {
                property = ReservoirData.GetType().GetProperty(REPLEN_LEVEL);
                _replenLevelLst[int.Parse(variable.Substring(6, 1)) - 1] = (bool)value;
                property.SetValue(_reservoirData, _replenLevelLst);
            }
            if (_variableInitializeDic.ContainsKey(variable) && !_variableInitializeDic[variable])
            {
                _variableInitializeDic[variable] = true;
            }
        }
        /// 
        /// 是否所有IO变量初始化完成
        /// 
        /// 
        private bool AllIoVariableInitialized()
        {
            foreach (string item in _variableInitializeDic.Keys)
            {
                if (!_variableInitializeDic[item])
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{item} is not initialized");
                    return false;
                }
            }
            return true;
        }
        public void Monitor()
        {
        }
        public void Reset()
        {
        }
        public void Terminate()
        {
        }
    }
}