chenkui 1 settimana fa
parent
commit
ac4ae70a0d

+ 2 - 0
PunkHPX8_Core/PunkHPX8_Core.csproj

@@ -68,6 +68,7 @@
     <Compile Include="ProcessCell.cs" />
     <Compile Include="RecipeResult.cs" />
     <Compile Include="RecipeToleranceChecker.cs" />
+    <Compile Include="ReservoirEnumData.cs" />
     <Compile Include="RobotArmPan.cs" />
     <Compile Include="SignalTowerItem.cs" />
     <Compile Include="StepItem.cs" />
@@ -79,6 +80,7 @@
     <Compile Include="RtState.cs" />
     <Compile Include="SCValue.cs" />
     <Compile Include="SerializeHelper.cs" />
+    <Compile Include="TemperatureEnumData.cs" />
     <Compile Include="TM.cs" />
     <Compile Include="UserRole.cs" />
     <Compile Include="ValveType.cs" />

+ 15 - 0
PunkHPX8_Core/ReservoirEnumData.cs

@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PunkHPX8_Core
+{
+    public enum ReservoirType
+    {
+        DegasMembrance,
+        HotStandard,
+        DegasInsolube
+    }
+}

+ 14 - 0
PunkHPX8_Core/TemperatureEnumData.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PunkHPX8_Core
+{
+    public enum TemperatureEnumData
+    {
+        DISABLE=0,
+        ENABLE =5
+    }
+}

+ 29 - 0
PunkHPX8_RT/Config/System.sccfg

@@ -333,4 +333,33 @@
 	<configs name="Linmot" nameView="Linmot">
 		<config default="5000" name="LinmotHoldoffMilliseconds" nameView="LinmotHoldoffMilliseconds" description="Linmot Hold off Milliseconds" max="10000" min="1" paramter="" tag="" unit="ms" type="Integer"></config>
 	</configs>
+	<configs name="Reservoir" nameView="Reservoir">
+		<config default="5000" name="CADefaultPumpSpeed" nameView="CADefaultPumpSpeed" description="CA pump default speed" max="7200" min="0" paramter="" tag="" unit="" type="Double"></config>
+		<config default="7200" name="CAMaxPumpSpeed" nameView="CAMaxPumpSpeed" description="CA Max Pump Speed" max="10000" min="1" paramter="" tag="" unit="rpm" type="Double"></config>
+		<config default="3" name="ANDefaultPumpSpeed" nameView="ANDefaultPumpSpeed" description="AN pump default speed" max="5" min="0" paramter="" tag="" unit="" type="Double"></config>
+		<config default="7200" name="ANMaxPumpSpeed" nameView="ANMaxPumpSpeed" description="AN Max Pump Speed" max="10000" min="1" paramter="" tag="" unit="rpm" type="Double"></config>
+		<config default="30" name="CellFlowUpdatePeriod" nameView="CellFlowUpdatePeriod" description="Cell Flow Update Period" max="60" min="0" paramter="" tag="" unit="s" type="Integer"/>
+		<config default="50" name="ReturnOpenDefaultPercentage" nameView="ReturnOpenDefaultPercentage" description="Return Valve default percentage" max="100" min="0" paramter="" tag="" unit="" type="Integer"></config>
+		<config default="20" name="LevelAvgSamples" nameView="LevelAvgSamples" description="Level average sample" max="100" min="0" paramter="" tag="" unit="" type="Integer"></config>
+		<configs name="Reservoir1" nameView="Reservoir1">
+			<config default="5.0" name="CAMainFlowFaultLow" nameView="CAMainFlowFaultLow" description="CA Main Flow Fault Low" max="30" min="0" paramter="" tag="" unit="" type="Double"></config>
+			<config default="10000" name="FlowFaultHoldOffTime" nameView="FlowFaultHoldOffTime" description="CA Flow Fault Hold Off Time" max="50000" min="0" paramter="" tag="" unit="ms" type="Integer"></config>
+			<config default="0,0,1.5022,94.782" name="LevelCurve" nameView="LevelCurve" description="Level curve" max="" min="" paramter="" tag="" unit="" type="String" />
+		</configs>
+		<configs name="Reservoir2" nameView="Reservoir2">
+			<config default="5.0" name="CAMainFlowFaultLow" nameView="CAMainFlowFaultLow" description="CA Main Flow Fault Low" max="30" min="0" paramter="" tag="" unit="" type="Double"></config>
+			<config default="10000" name="FlowFaultHoldOffTime" nameView="FlowFaultHoldOffTime" description="CA Flow Fault Hold Off Time" max="50000" min="0" paramter="" tag="" unit="ms" type="Integer"></config>
+			<config default="0,0,1.5022,94.782" name="LevelCurve" nameView="LevelCurve" description="Level curve" max="" min="" paramter="" tag="" unit="" type="String" />
+		</configs>
+		<configs name="Reservoir3" nameView="Reservoir3">
+			<config default="5.0" name="CAMainFlowFaultLow" nameView="CAMainFlowFaultLow" description="CA Main Flow Fault Low" max="30" min="0" paramter="" tag="" unit="" type="Double"></config>
+			<config default="10000" name="FlowFaultHoldOffTime" nameView="FlowFaultHoldOffTime" description="CA Flow Fault Hold Off Time" max="50000" min="0" paramter="" tag="" unit="ms" type="Integer"></config>
+			<config default="0,0,1.5022,94.782" name="LevelCurve" nameView="LevelCurve" description="Level curve" max="" min="" paramter="" tag="" unit="" type="String" />
+		</configs>
+		<configs name="Reservoir4" nameView="Reservoir4">
+			<config default="5.0" name="CAMainFlowFaultLow" nameView="CAMainFlowFaultLow" description="CA Main Flow Fault Low" max="30" min="0" paramter="" tag="" unit="" type="Double"></config>
+			<config default="10000" name="FlowFaultHoldOffTime" nameView="FlowFaultHoldOffTime" description="CA Flow Fault Hold Off Time" max="50000" min="0" paramter="" tag="" unit="ms" type="Integer"></config>
+			<config default="0,0,1.5022,94.782" name="LevelCurve" nameView="LevelCurve" description="Level curve" max="" min="" paramter="" tag="" unit="" type="String" />
+		</configs>
+	</configs>
 </root>

+ 117 - 0
PunkHPX8_RT/Devices/Reservoir/ANPumpOnRoutine.cs

@@ -0,0 +1,117 @@
+using Aitex.Core.RT.Device;
+using Aitex.Core.RT.Log;
+using Aitex.Core.RT.Routine;
+using Aitex.Core.RT.SCCore;
+using MECF.Framework.Common.Beckhoff.ModuleIO;
+using MECF.Framework.Common.Routine;
+using MECF.Framework.Common.ToolLayout;
+using MECF.Framework.Common.TwinCat;
+using PunkHPX8_Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PunkHPX8_RT.Devices.Reservoir
+{
+    public class ANPumpOnRoutine : RoutineBase, IRoutine
+    {
+        private enum ANPumpStep
+        {
+            PumpSpeed,
+            Delay,
+            CheckFlow,
+            End
+        }
+
+        #region 内部变量
+        /// <summary>
+        /// 默认泵速
+        /// </summary>
+        private double _anPumpSpeed = 5000;
+        /// <summary>
+        /// flow fault hold off时长
+        /// </summary>
+        private int _flowFaultHoldOffTime = 10000;
+        /// <summary>
+        /// 阳极最小流量 
+        /// </summary>
+        private double _anMinFlow = 0.2;
+        /// <summary>
+        /// Reservoir设备
+        /// </summary>
+        private DMReservoirDevice _device;
+        #endregion
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="module"></param>
+        public ANPumpOnRoutine(string module) : base(module)
+        {
+        }
+
+
+        /// <summary>
+        /// 中止
+        /// </summary>
+        public void Abort()
+        {
+            Runner.Stop("Manual abort");
+        }
+        /// <summary>
+        /// 监控
+        /// </summary>
+        /// <returns></returns>
+        public RState Monitor()
+        {
+            Runner.Run(ANPumpStep.PumpSpeed, () => { return ANPumpSpeed(_anPumpSpeed); }, _delay_1ms)
+                .Delay(ANPumpStep.Delay, _flowFaultHoldOffTime)
+                .Run(ANPumpStep.CheckFlow,CheckAllFlow,_delay_1ms)
+                .End(ANPumpStep.End, NullFun, _delay_1ms);
+            return Runner.Status;
+        }
+        /// <summary>
+        /// Pump Speed
+        /// </summary>
+        /// <param name="speed"></param>
+        /// <returns></returns>
+        private bool ANPumpSpeed(double speed)
+        {            
+            return _device.AnPumpSpeed(speed);
+        }
+        /// <summary>
+        /// 检验所有流量
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckAllFlow()
+        {
+            double anTotalFlow = _device.ReservoirData.AnFlow;
+
+            if(anTotalFlow<=_anMinFlow)
+            {
+                _device.AnPumpOff();
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"total flow {anTotalFlow} is not over {_anMinFlow}");
+                return false;
+            }
+            else
+            {
+                LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"total flow {anTotalFlow} is over {_anMinFlow}");
+                return true;
+            }
+        }
+        /// <summary>
+        /// 启动
+        /// </summary>
+        /// <param name="objs"></param>
+        /// <returns></returns>
+        public RState Start(params object[] objs)
+        {
+            _device = DEVICE.GetDevice<DMReservoirDevice>(Module);
+            _anPumpSpeed = SC.GetValue<double>("Reservoir.ANDefaultPumpSpeed");
+            _flowFaultHoldOffTime = SC.GetValue<int>($"Reservoir.{Module}.FlowFaultHoldOffTime");
+            return Runner.Start(Module, "AN Pump On");
+        }
+    }
+}

+ 156 - 0
PunkHPX8_RT/Devices/Reservoir/CAPumpOnRoutine.cs

@@ -0,0 +1,156 @@
+using Aitex.Core.RT.Device;
+using Aitex.Core.RT.Log;
+using Aitex.Core.RT.Routine;
+using Aitex.Core.RT.SCCore;
+using MECF.Framework.Common.Beckhoff.ModuleIO;
+using MECF.Framework.Common.Routine;
+using MECF.Framework.Common.ToolLayout;
+using MECF.Framework.Common.TwinCat;
+using PunkHPX8_Core;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PunkHPX8_RT.Devices.Reservoir
+{
+    public class CAPumpOnRoutine : RoutineBase, IRoutine
+    {
+        private enum CAPumpStep
+        {
+            PumpSpeed,
+            PumpEnable,
+            Delay,
+            CheckRunning,
+            FlowDelay,
+            CheckFlow,
+            End
+        }
+        #region 常量
+        private const string CA_PUMP_SPEED = "CAPumpSpeed";
+        private const string CA_PUMP_RUNNING = "CAPumpRunning";
+        private const string CA_HED_FLOW = "CAHedFlow";
+        private const string CA_PUMP_ENABLE = "CAPumpEnable";
+        private const string CA_BY_PASS = "CAByPass";
+        #endregion
+
+        #region 内部变量
+        private double _caPumpSpeed = 5000;
+        private int _flowFaultHoldOffTime = 10000;
+        private double _caMainFlowFaultLow = 5.0;
+        private double _caHedFlowLowLimit = 3.0;
+        private ReservoirDevice _device;
+        private int _caOpenValveCount = 0;
+        #endregion
+
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="module"></param>
+        public CAPumpOnRoutine(string module) : base(module)
+        {
+        }
+
+
+        /// <summary>
+        /// 中止
+        /// </summary>
+        public void Abort()
+        {
+            Runner.Stop("Manual abort");
+        }
+        /// <summary>
+        /// 监控
+        /// </summary>
+        /// <returns></returns>
+        public RState Monitor()
+        {
+            Runner.Run(CAPumpStep.PumpSpeed, () => { return CAPumpSpeed(_caPumpSpeed); }, _delay_2s)
+                .Run(CAPumpStep.PumpEnable, CAPumpEnable, _delay_1ms)
+                .Delay(CAPumpStep.Delay, 500)
+                .Run(CAPumpStep.CheckRunning, CheckCAPumpRunning, _delay_1ms)
+                .Delay(CAPumpStep.FlowDelay, _flowFaultHoldOffTime)
+                .Run(CAPumpStep.CheckFlow, CheckAllFlow, _delay_1ms)
+                .End(CAPumpStep.End, NullFun, _delay_1ms);
+            return Runner.Status;
+        }
+        /// <summary>
+        /// Pump Speed
+        /// </summary>
+        /// <param name="speed"></param>
+        /// <returns></returns>
+        private bool CAPumpSpeed(double speed)
+        {
+            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{CA_PUMP_SPEED}");
+            return BeckhoffIOManager.Instance.WriteIoValue(ioName, speed);
+        }
+        /// <summary>
+        /// Pump Enable
+        /// </summary>
+        /// <param name="speed"></param>
+        /// <returns></returns>
+        private bool CAPumpEnable()
+        {
+            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{CA_PUMP_ENABLE}");
+            return BeckhoffIOManager.Instance.WriteIoValue(ioName, true);
+        }
+        /// <summary>
+        /// Pump disable
+        /// </summary>
+        /// <returns></returns>
+        private bool CAPumpDisable()
+        {
+            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{CA_PUMP_ENABLE}");
+            return BeckhoffIOManager.Instance.WriteIoValue(ioName, false);
+        }
+        /// <summary>
+        /// 检验CA Running Sensor
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckCAPumpRunning()
+        {
+            if(!_device.ReservoirData.CaPumpRunning)
+            {
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, "CA Pump Running Sensor is false");
+                return false;
+            }
+            return true;
+        }
+        /// <summary>
+        /// 检验所有流量
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckAllFlow()
+        {
+            double flow = _device.ReservoirData.CaFlow;
+
+            if(flow<=_caMainFlowFaultLow)
+            {
+                CAPumpDisable();
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"total flow {flow} is not over {_caMainFlowFaultLow}");
+                return false;
+            }
+            else
+            {
+                LOG.WriteLog(eEvent.INFO_RESERVOIR, Module, $"total flow {flow} is over {_caMainFlowFaultLow}");
+            }
+            return true;
+        }
+        /// <summary>
+        /// 启动
+        /// </summary>
+        /// <param name="objs"></param>
+        /// <returns></returns>
+        public RState Start(params object[] objs)
+        {
+            _device = DEVICE.GetDevice<ReservoirDevice>(Module);
+            _caPumpSpeed = SC.GetValue<double>("Reservoir.CADefaultPumpSpeed");
+            _flowFaultHoldOffTime = SC.GetValue<int>($"Reservoir.{Module}.FlowFaultHoldOffTime");
+            _caMainFlowFaultLow = SC.GetValue<double>($"Reservoir.{Module}.CAMainFlowFaultLow");
+            _caHedFlowLowLimit = SC.GetValue<double>($"Reservoir.{Module}.HEDFlowLowLimit");
+            _caOpenValveCount = 0;
+            return Runner.Start(Module, "CA Pump On");
+        }
+    }
+}

+ 145 - 2
PunkHPX8_RT/Devices/Reservoir/DMReservoirDevice.cs

@@ -1,4 +1,12 @@
-using System;
+using Aitex.Core.RT.Log;
+using Aitex.Core.RT.OperationCenter;
+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.Equipment;
+using MECF.Framework.Common.TwinCat;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -8,6 +16,26 @@ namespace PunkHPX8_RT.Devices.Reservoir
 {
     public class DMReservoirDevice : ReservoirDevice
     {
+        #region 常量
+        private const string AN_PUMP = "ANPump";
+        #endregion
+        #region 内部变量
+        /// <summary>
+        /// 默认泵速
+        /// </summary>
+        private double _anPumpSpeed = 5000;
+        #endregion
+
+        #region Trigger
+        /// <summary>
+        /// low WaterLevel trigger
+        /// </summary>
+        private R_TRIG _anWaterLevelLowerTrigger = new R_TRIG();
+        /// <summary>
+        /// low WaterLevel trigger
+        /// </summary>
+        private R_TRIG _anWaterLevelHighTrigger = new R_TRIG();
+        #endregion
         /// <summary>
         /// 构造函数
         /// </summary>
@@ -15,7 +43,9 @@ namespace PunkHPX8_RT.Devices.Reservoir
         public DMReservoirDevice(string moduleName) : base(moduleName)
         {
         }
-
+        /// <summary>
+        /// 订阅变量
+        /// </summary>
         protected override void SubscribeValueAction()
         {
             base.SubscribeValueAction();
@@ -25,5 +55,118 @@ namespace PunkHPX8_RT.Devices.Reservoir
             IoSubscribeUpdateVariable(AN_PUMP_SPEED);
             IoSubscribeUpdateVariable(DEGAS_ENABLE);
         }
+        /// <summary>
+        /// 订阅Operation
+        /// </summary>
+        protected override void InitializeOperation()
+        {
+            base.InitializeOperation(); 
+            OP.Subscribe($"{Module}.AnPumpOn", AnPumpOnOperation);
+            OP.Subscribe($"{Module}.ANPumpSpeed", ANPumpSpeedOperation);
+            OP.Subscribe($"{Module}.AnPumpOff", AnPumpOffOperation);
+        }
+
+        #region AnPump
+        /// <summary>
+        /// AN Pump调速
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool ANPumpSpeedOperation(string cmd, object[] args)
+        {
+            double anMaxPumpSpeed = 0;
+            if (SC.ContainsItem("Reservoir.ANMaxPumpSpeed"))
+            {
+                anMaxPumpSpeed = SC.GetValue<double>("Reservoir.ANMaxPumpSpeed");
+            }
+            if (double.TryParse(args[0].ToString(), out double speed))
+            {
+                _anPumpSpeed = speed;
+                if (_anPumpSpeed > anMaxPumpSpeed)
+                {
+                    LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"AN pump speed:{_anPumpSpeed} is over AN max pump speed {anMaxPumpSpeed}!");
+                    return false;
+                }
+                return AnPumpSpeed(_anPumpSpeed);
+            }
+            else
+            {
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"{args[0]} is nor invalid speed");
+                return false;
+            }
+        }
+        /// <summary>
+        /// 设置AN泵速
+        /// </summary>
+        /// <param name="caPumpSpeed"></param>
+        /// <returns></returns>
+        public bool AnPumpSpeed(double anPumpSpeed)
+        {
+            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{AN_PUMP}");
+            return BeckhoffIOManager.Instance.WriteIoValue(ioName, anPumpSpeed);
+        }
+        /// <summary>
+        /// 阳极Pump On
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool AnPumpOnOperation(string cmd, object[] args)
+        {
+            double caPumpSpeed = SC.GetValue<double>("Reservoir.ANDefaultPumpSpeed");
+            bool result = AnPumpSpeed(caPumpSpeed);
+            if (result)
+            {
+                string enableIOName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{AN_PUMP_ENABLE}");
+                return BeckhoffIOManager.Instance.WriteIoValue(enableIOName, true);
+            }
+            else
+            {
+                return false;
+            }
+        }
+        /// <summary>
+        /// 阳极Pump Off
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool AnPumpOffOperation(string cmd, object[] args)
+        {
+            return AnPumpOff();
+        }
+        /// <summary>
+        /// 关闭阳极Pump
+        /// </summary>
+        /// <returns></returns>
+        public bool AnPumpOff()
+        {
+            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{AN_PUMP_ENABLE}");
+            return BeckhoffIOManager.Instance.WriteIoValue(ioName, false);
+        }
+        #endregion
+
+        /// <summary>
+        /// 水位监控
+        /// </summary>
+        protected override void WaterLevelMonitor()
+        {
+            base.WaterLevelMonitor();
+        }
+        /// <summary>
+        /// 补水监控
+        /// </summary>
+        protected override void AutoDireplenMonitor()
+        {
+            base.AutoDireplenMonitor();
+        }
+        /// <summary>
+        /// 阳极补水
+        /// </summary>
+        private void AnDireplenMonitor()
+        {
+            
+        }
     }
 }

+ 13 - 1
PunkHPX8_RT/Devices/Reservoir/HSReservoirDevice.cs

@@ -8,6 +8,9 @@ namespace PunkHPX8_RT.Devices.Reservoir
 {
     public class HSReservoirDevice : ReservoirDevice
     {
+        #region 内部变量
+        
+        #endregion
         /// <summary>
         /// 构造函数
         /// </summary>
@@ -15,7 +18,9 @@ namespace PunkHPX8_RT.Devices.Reservoir
         public HSReservoirDevice(string moduleName) : base(moduleName)
         {
         }
-
+        /// <summary>
+        /// 
+        /// </summary>
         protected override void SubscribeValueAction()
         {
             base.SubscribeValueAction();
@@ -24,5 +29,12 @@ namespace PunkHPX8_RT.Devices.Reservoir
             IoSubscribeUpdateVariable(HED_FLOW);
             IoSubscribeUpdateVariable(HED_FLOW_ENABLE);
         }
+        /// <summary>
+        /// 初始化operation
+        /// </summary>
+        protected override void InitializeOperation()
+        {
+            base.InitializeOperation();
+        }
     }
 }

+ 35 - 0
PunkHPX8_RT/Devices/Reservoir/LevelCurveManager.cs

@@ -0,0 +1,35 @@
+using Aitex.Core.Util;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PunkHPX8_RT.Devices.Reservoir
+{
+    public class LevelCurveManager : Singleton<LevelCurveManager>
+    {
+        /// <summary>
+        /// 计算Level
+        /// </summary>
+        /// <param name="waterLevel"></param>
+        /// <param name="levelCurve"></param>
+        /// <returns></returns>
+        public double CalculateLevelByWaterLevel(double waterLevel,string levelCurve)
+        {
+            string[] strAry= levelCurve.Split(',',',');
+            if (strAry.Length >= 3)
+            {
+                double.TryParse(strAry[strAry.Length - 1], out double c);
+                double.TryParse(strAry[strAry.Length-2], out double b);
+                double.TryParse(strAry[strAry.Length-3], out double a);
+
+                return a*Math.Pow(waterLevel,2)+b*waterLevel+c;
+            }
+            else
+            {
+                return 0;
+            }
+        }
+    }
+}

+ 82 - 0
PunkHPX8_RT/Devices/Reservoir/ReservoirANPumpSpeedHelper.cs

@@ -0,0 +1,82 @@
+using Aitex.Core.RT.Device;
+using Aitex.Core.RT.SCCore;
+using MECF.Framework.Common.Equipment;
+using MECF.Framework.Common.RecipeCenter;
+using MECF.Framework.Common.ToolLayout;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PunkHPX8_RT.Devices.Reservoir
+{
+    public class ReservoirANPumpSpeedHelper
+    {
+        #region 内部变量
+        /// <summary>
+        /// 时间
+        /// </summary>
+        private DateTime _updateTime = DateTime.Now;
+        /// <summary>
+        /// 模块名称
+        /// </summary>
+        private string _moduleName;
+        /// <summary>
+        /// CA流量总和
+        /// </summary>
+        private double _caTotalFlow = 0;
+        /// <summary>
+        /// 设备对象
+        /// </summary>
+        DMReservoirDevice _device;
+        #endregion
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="moduleName"></param>
+        public ReservoirANPumpSpeedHelper(string moduleName, DMReservoirDevice reservoirDevice)
+        {
+            _moduleName = moduleName;
+            _device = reservoirDevice;
+        }
+        /// <summary>
+        /// 监控
+        /// </summary>
+        public void Monitor(ResRecipe resRecipe)
+        {
+            int cellFlowUpdatePeriod = SC.GetValue<int>("Reservoir.CellFlowUpdatePeriod");
+            if (DateTime.Now.Subtract(_updateTime).TotalSeconds > cellFlowUpdatePeriod)
+            {
+                _updateTime = DateTime.Now;
+                AdjustAnPumpSpeed(resRecipe);
+            }
+        }
+        /// <summary>
+        /// 调节阳极泵速
+        /// </summary>
+        private void AdjustAnPumpSpeed(ResRecipe resRecipe)
+        {
+            if (_device.ReservoirData.AnPumpEnable)
+            {
+                double anPumpSpeed = _device.ReservoirData.AnPumpSpeed;
+                double averageANFlow = _device.ReservoirData.AnFlow;
+                if (averageANFlow == 0)
+                {
+                    return;
+                }
+                double anPumpMaxSpeed = SC.GetValue<double>("Reservoir.ANMaxPumpSpeed");
+                double anFlowDelta = resRecipe.ANFlowSetPoint - averageANFlow;
+                double newANPumpSpeed = 1.8 * anFlowDelta + anPumpSpeed;
+                if (newANPumpSpeed <= 0||newANPumpSpeed>=anPumpMaxSpeed)
+                {
+                    return;
+                }
+                if (Math.Abs(newANPumpSpeed - anPumpSpeed) >= 0.01)
+                {
+                    _device.AnPumpSpeed(newANPumpSpeed);
+                }
+            }
+        }
+    }
+}

+ 82 - 0
PunkHPX8_RT/Devices/Reservoir/ReservoirCAPumpSpeedHelper.cs

@@ -0,0 +1,82 @@
+using Aitex.Core.RT.Device;
+using Aitex.Core.RT.SCCore;
+using MECF.Framework.Common.Equipment;
+using MECF.Framework.Common.RecipeCenter;
+using MECF.Framework.Common.ToolLayout;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PunkHPX8_RT.Devices.Reservoir
+{
+    public class ReservoirCAPumpSpeedHelper
+    {
+        #region 内部变量
+        /// <summary>
+        /// 时间
+        /// </summary>
+        private DateTime _updateTime = DateTime.Now;
+        /// <summary>
+        /// 模块名称
+        /// </summary>
+        private string _moduleName;
+        /// <summary>
+        /// CA流量总和
+        /// </summary>
+        private double _caTotalFlow = 0;
+        /// <summary>
+        /// 设备对象
+        /// </summary>
+        ReservoirDevice _device;
+        #endregion
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="moduleName"></param>
+        public ReservoirCAPumpSpeedHelper(string moduleName, ReservoirDevice reservoirDevice)
+        {
+            _moduleName = moduleName;
+            _device = reservoirDevice;
+        }
+        /// <summary>
+        /// 监控
+        /// </summary>
+        public void Monitor(ResRecipe resRecipe)
+        {
+            int cellFlowUpdatePeriod = SC.GetValue<int>("Reservoir.CellFlowUpdatePeriod");
+            if (DateTime.Now.Subtract(_updateTime).TotalSeconds > cellFlowUpdatePeriod)
+            {
+                _updateTime = DateTime.Now;
+                AdjustCAPumpSpeed(resRecipe);
+            }
+        }
+        /// <summary>
+        /// 调节阳极泵速
+        /// </summary>
+        private void AdjustCAPumpSpeed(ResRecipe resRecipe)
+        {
+            if (_device.ReservoirData.CaPumpEnable)
+            {
+                double caPumpSpeed = _device.ReservoirData.CaPumpSpeed;
+                double averageCAFlow = _device.ReservoirData.CaFlow;
+                if (averageCAFlow == 0)
+                {
+                    return;
+                }
+                double caPumpMaxSpeed = SC.GetValue<double>("Reservoir.CAMaxPumpSpeed");
+                double caFlowDelta = resRecipe.CAFlowSetPoint - averageCAFlow;
+                double newCAPumpSpeed = 330 * caFlowDelta + caPumpSpeed;
+                if (newCAPumpSpeed <= 0 || newCAPumpSpeed > caPumpMaxSpeed)
+                {
+                    return;
+                }
+                if (Math.Abs(newCAPumpSpeed - caPumpSpeed) >= 10)
+                {
+                    _device.CAPumpSpeed(newCAPumpSpeed);
+                }
+            }
+        }
+    }
+}

+ 664 - 2
PunkHPX8_RT/Devices/Reservoir/ReservoirDevice.cs

@@ -2,17 +2,29 @@
 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.Alarm;
+using MECF.Framework.Common.Beckhoff.ModuleIO;
 using MECF.Framework.Common.CommonData.Reservoir;
 using MECF.Framework.Common.IOCore;
 using MECF.Framework.Common.Persistent.Reservoirs;
+using MECF.Framework.Common.RecipeCenter;
+using MECF.Framework.Common.ToolLayout;
 using MECF.Framework.Common.TwinCat;
 using MECF.Framework.Common.Utilities;
+using PunkHPX8_Core;
+using PunkHPX8_RT.Devices.Facilities;
+using PunkHPX8_RT.Devices.Safety;
+using PunkHPX8_RT.Devices.Temperature;
 using PunkHPX8_RT.Modules;
+using PunkHPX8_RT.Modules.Reservoir;
 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
+using System.ServiceModel.Security;
 using System.Text;
 using System.Threading.Tasks;
 
@@ -67,6 +79,63 @@ namespace PunkHPX8_RT.Devices.Reservoir
         /// 定时器
         /// </summary>
         private PeriodicJob _periodicJob;
+        /// <summary>
+        /// 阴极Pump速度
+        /// </summary>
+        private double _caPumpSpeed = 0;
+        /// <summary>
+        /// Return Valve比例
+        /// </summary>
+        private double _returnValvePercent = 0.5;
+        /// <summary>
+        /// CA Level取样平均值
+        /// </summary>
+        private double _avgCALevel;
+        /// <summary>
+        /// CA Level取样队列
+        /// </summary>
+        private Queue<double> _CALevelSamples;
+        /// <summary>
+        /// CA level计算平均值取样数
+        /// </summary>
+        private int _levelSampleCount;
+        /// <summary>
+        /// Recipe
+        /// </summary>
+        private ResRecipe _resRecipe;
+
+        /// <summary>
+        /// 累计补水是否超时
+        /// </summary>
+        private bool _isDIReplenMaxTimeOut = false;
+        /// <summary>
+        /// 单次补水是否超时
+        /// </summary>
+        private bool _isDIReplenPerfillTimeOut = false;
+        /// <summary>
+        /// 注水Helper
+        /// </summary>
+        private ReservoirDiReplenHelper _direplenHelper;
+        #endregion
+
+        #region Trigger
+        /// <summary>
+        /// low WaterLevel trigger
+        /// </summary>
+        private R_TRIG _caWaterLevelLowerTrigger=new R_TRIG();
+        /// <summary>
+        /// low WaterLevel trigger
+        /// </summary>
+        private R_TRIG _caWaterLevelHighTrigger = new R_TRIG();
+
+        #endregion
+
+        #region 共享变量
+        protected DiReplenOperation _currentDireplenOperation = DiReplenOperation.None;
+        /// <summary>
+        /// 手动注水时间(秒)
+        /// </summary>
+        protected int _manualReplenSecond = 0;
         #endregion
 
         #region 属性
@@ -87,6 +156,18 @@ namespace PunkHPX8_RT.Devices.Reservoir
         /// 数据
         /// </summary>
         public ReservoirData ReservoirData { get { return _reservoirData; } }
+        /// <summary>
+        /// 检验阴极是否highlevel
+        /// </summary>
+        public bool IsCAHighLevel { get { return CheckCAHighLevelStatus(); } }
+        /// <summary>
+        /// 检验阴极是否lowlevel
+        /// </summary>
+        public bool IsCALowLevel { get { return CheckCALowLevelStatus(); } }
+        /// <summary>
+        /// 当前Recipe
+        /// </summary>
+        public ResRecipe Recipe { get { return _resRecipe; } }
         #endregion
         /// <summary>
         /// 构造函数
@@ -95,6 +176,9 @@ namespace PunkHPX8_RT.Devices.Reservoir
         /// <param name="name"></param>
         public ReservoirDevice(string moduleName) : base(moduleName, moduleName, moduleName, moduleName)
         {
+            _levelSampleCount = SC.GetValue<int>("Reservoir.LevelAvgSamples");
+            _levelSampleCount = _levelSampleCount == 0 ? 20 : _levelSampleCount;
+            _CALevelSamples = new Queue<double>(_levelSampleCount);
         }
         /// <summary>
         /// 初始化
@@ -120,6 +204,16 @@ namespace PunkHPX8_RT.Devices.Reservoir
             {
                 LOG.WriteLog(eEvent.ERR_RESERVOIR, Module.ToString(), "Persistent Value Object is not exist");
             }
+            _persistentValue = ReservoirsPersistentManager.Instance.GetReservoirsPersistentValue(Module);
+            if (_persistentValue == null)
+            {
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, "Persistent Value Object is not exist");
+            }
+            if (!string.IsNullOrEmpty(_persistentValue.Recipe))
+            {
+                _resRecipe = RecipeFileManager.Instance.LoadGenericityRecipe<ResRecipe>(_persistentValue.Recipe);
+            }
+            _direplenHelper = new ReservoirDiReplenHelper(Module, _persistentValue);
         }
         /// <summary>
         /// 初始化Routine
@@ -139,13 +233,16 @@ namespace PunkHPX8_RT.Devices.Reservoir
         /// <summary>
         /// 初始化Operation
         /// </summary>
-        private void InitializeOperation()
+        protected virtual 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}.ProductionModeAction", ProductionModeOperation); 
+            OP.Subscribe($"{Module}.CAPumpOn", CAPumpOn);
+            OP.Subscribe($"{Module}.CAPumpSpeed", CAPumpSpeedOperation);
+            OP.Subscribe($"{Module}.CAPumpOff", CAPumpOff);
         }
         /// <summary>
         /// 订阅变量数值发生变化
@@ -189,6 +286,11 @@ namespace PunkHPX8_RT.Devices.Reservoir
             if (property != null)
             {
                 property.SetValue(_reservoirData, value);
+                if (variable == CA_WATER_LEVEL)
+                {
+                    string caLevelCurve = SC.GetStringValue($"Reservoir.{Module}.CALevelCurve");
+                    ReservoirData.CaLevel = LevelCurveManager.Instance.CalculateLevelByWaterLevel(ReservoirData.CaWaterLevel, caLevelCurve);
+                }
             }
             if (_variableInitializeDic.ContainsKey(variable) && !_variableInitializeDic[variable])
             {
@@ -201,8 +303,170 @@ namespace PunkHPX8_RT.Devices.Reservoir
         /// <returns></returns>
         protected virtual bool OnTimer()
         {
+            CalculateCALevel();
+            WaterLevelMonitor();
+            DireplenMonitor();
             return true;
         }
+        #region timer
+        /// <summary>
+        /// 计算CA
+        /// </summary>
+        private void CalculateCALevel()
+        {
+            if (ReservoirData != null)
+            {
+                if (_CALevelSamples.Count >= _levelSampleCount)
+                {
+                    _CALevelSamples.Dequeue();
+                    _CALevelSamples.Enqueue(ReservoirData.CaLevel);
+                }
+                else
+                {
+                    _CALevelSamples.Enqueue(ReservoirData.CaLevel);
+                }
+                _avgCALevel = _CALevelSamples.Count > 0 ? _CALevelSamples.Average() : 0;
+            }
+        }
+
+        /// <summary>
+        /// WaterLevel监控
+        /// </summary>
+        protected virtual void WaterLevelMonitor()
+        {
+            _caWaterLevelLowerTrigger.CLK = IsCALowLevel;
+            if (_caWaterLevelLowerTrigger.Q)
+            {
+                ReservoirEntity reservoirEntity = Singleton<RouteManager>.Instance.GetModule<ReservoirEntity>(Module);
+
+                string reason = $"Current CAWaterlevel:{ReservoirData.CaWaterLevel} is lower than  CALowLevel Config:{SC.GetValue<double>($"Reservoir.{Module}.CALowLevel")}";
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, reason);
+                CALowLevelOperation();
+                if (reservoirEntity.IsAuto && !AlarmListManager.Instance.IsContainDataError(Module, "CAWaterLevel"))
+                {
+                    AlarmListManager.Instance.AddDataError(Module,
+                        $"CAWaterLevel", $"Current CAWaterlevel:{ReservoirData.CaWaterLevel} is lower than  CALowLevel Config:{SC.GetValue<double>($"Reservoir.{Module}.CALowLevel")}");
+                }
+                if (!reservoirEntity.IsError) reservoirEntity.PostMsg(ReservoirMsg.Error);
+            }
+
+            _caWaterLevelLowerTrigger.CLK = IsCAHighLevel;
+            if (_caWaterLevelHighTrigger.Q)
+            {
+                HighLevelOperation();
+            }
+        }
+
+        /// <summary>
+        /// CA Low Level触发对应操作
+        /// </summary>
+        private void CALowLevelOperation()
+        {
+            ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(Module);
+            if (_reservoirData.CaPumpEnable)
+            {
+                CAPumpOff("", null);
+            }
+            //禁用TC
+            if (!string.IsNullOrEmpty(reservoirItem.TCID))
+            {
+                TemperatureController temperatureController = DEVICE.GetDevice<TemperatureController>(reservoirItem.TCID);
+                if (temperatureController != null && temperatureController.TemperatureData.ControlOperationModel == (int)TemperatureEnumData.ENABLE)
+                {
+                    temperatureController.DisableOperation("", null);
+                }
+            }
+
+            ReservoirEntity reservoirEntity = Singleton<RouteManager>.Instance.GetModule<ReservoirEntity>(Module);
+
+            if (!reservoirEntity.IsError) reservoirEntity.PostMsg(ReservoirMsg.Error);
+        }
+        /// <summary>
+        /// High Level Common Operation
+        /// </summary>
+        private void HighLevelOperation()
+        {
+            ReservoirEntity reservoirEntity = Singleton<RouteManager>.Instance.GetModule<ReservoirEntity>(Module);
+            SystemFacilities systemFacilities = DEVICE.GetDevice<SystemFacilities>("System.Facilities");
+            if (systemFacilities != null)
+            {
+                if (systemFacilities.DIFillEnable) systemFacilities.DiFillDisableOperation("DIFillDisableOpeartion", null);
+                if (systemFacilities.DIReplenEnable) systemFacilities.DiReplenDisableOperation("DiReplenDisableOperation", null);
+
+                if (_reservoirData.CaDiReplen)
+                {
+                    _currentDireplenOperation = DiReplenOperation.None;
+                    CADiReplenOff("", null);
+                }
+            }
+            if (!reservoirEntity.IsError) reservoirEntity.PostMsg(ReservoirMsg.Error);
+            if (reservoirEntity.IsAuto && !AlarmListManager.Instance.IsContainDataError(Module, "CAWaterLevel"))
+            {
+                AlarmListManager.Instance.AddDataError(Module,
+                    $"CAWaterLevel", $"Current CAWaterlevel:{ReservoirData.CaWaterLevel} is large than  CAHighLevel Config:{SC.GetValue<double>($"Reservoir.{Module}.CAHighLevel")}");
+            }
+        }
+        /// <summary>
+        /// Direplen监控
+        /// </summary>
+        private void DireplenMonitor()
+        {
+            var facilities = DEVICE.GetDevice<SystemFacilities>("System.Facilities");
+            //补水监控
+            if (_direplenHelper != null)
+            {
+                _direplenHelper.MonitorPeriodTime(ref _isDIReplenMaxTimeOut);
+                if (!_isDIReplenMaxTimeOut && !_isDIReplenPerfillTimeOut && facilities.DIFillEnable)
+                {
+                    AutoDireplenMonitor();
+                }
+            }
+        }
+        /// <summary>
+        /// 自动补水
+        /// </summary>
+        protected virtual void AutoDireplenMonitor()
+        {
+            if (_currentDireplenOperation == DiReplenOperation.ManualCADiReplen)
+            {
+                bool result = _direplenHelper.MonitorManualDiReplenComplete(_manualReplenSecond, CADiReplenOff, ref _isDIReplenMaxTimeOut);
+                if (result)
+                {
+                    _currentDireplenOperation = DiReplenOperation.None;
+                }
+            }
+            if (_currentDireplenOperation == DiReplenOperation.AutoCADiReplen)
+            {
+                AutoDiReplenMonitor(CADiReplenOff, _reservoirData.CaLevel, _resRecipe.ReservoirCALevel, _resRecipe.DIReplenEnable,
+                    _resRecipe.DIReplenTimeRate, _resRecipe.DIReplenCurrentRate);
+            }
+        }
+        /// <summary>
+        /// 自动注水监控
+        /// </summary>
+        /// <param name="direplenOff"></param>
+        /// <param name="level"></param>
+        /// <param name="recipeLevel"></param>
+        private void AutoDiReplenMonitor(Func<string, object[], bool> direplenOff, double level, double recipeLevel, bool replenEnable,
+            int direplenTimeRate, int direplenCurrentRate)
+        {
+            bool result = _direplenHelper.AutoDiReplenMonitorTimeOut(direplenOff, ref _isDIReplenMaxTimeOut, ref _isDIReplenPerfillTimeOut);
+            if (result)
+            {
+                _currentDireplenOperation = DiReplenOperation.None;
+            }
+            else
+            {
+                //按液位补水
+                result = _direplenHelper.AutoDiReplenMonitorComplete(level, recipeLevel, replenEnable, direplenTimeRate, direplenCurrentRate, direplenOff);
+                if (result)
+                {
+                    _currentDireplenOperation = DiReplenOperation.None;
+                }
+            }
+        }
+
+        #endregion
 
         #region Mode switch
         /// <summary>
@@ -298,6 +562,395 @@ namespace PunkHPX8_RT.Devices.Reservoir
         public void Monitor()
         {
         }
+        #region CA Pump
+        /// <summary>
+        /// CA Pump调速
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool CAPumpSpeedOperation(string cmd, object[] args)
+        {
+            double caMaxPumpSpeed = 0;
+            if (SC.ContainsItem("Reservoir.CAMaxPumpSpeed"))
+            {
+                caMaxPumpSpeed = SC.GetValue<double>("Reservoir.CAMaxPumpSpeed");
+            }
+            if (double.TryParse(args[0].ToString(), out double speed))
+            {
+                _caPumpSpeed = speed;
+                if (_caPumpSpeed > caMaxPumpSpeed)
+                {
+                    LOG.WriteLog(eEvent.WARN_METAL, Module, $"CA pump speed:{_caPumpSpeed} is over CA max pump speed {caMaxPumpSpeed}!");
+                    return false;
+                }
+                return CAPumpSpeed(_caPumpSpeed);
+            }
+            else
+            {
+                LOG.WriteLog(eEvent.ERR_METAL, Module, $"{args[0]} is nor invalid speed");
+                return false;
+            }
+        }
+        /// <summary>
+        /// 设置阴极泵速
+        /// </summary>
+        /// <param name="caPumpSpeed"></param>
+        /// <returns></returns>
+        public bool CAPumpSpeed(double caPumpSpeed)
+        {
+            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{CA_PUMP_SPEED}");
+            return BeckhoffIOManager.Instance.WriteIoValue(ioName, caPumpSpeed);
+        }
+        /// <summary>
+        /// 阴极Pump On
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool CAPumpOn(string cmd, object[] args)
+        {
+            double caPumpSpeed = SC.GetValue<double>("Reservoir.CADefaultPumpSpeed");
+            bool result = CAPumpSpeed(caPumpSpeed);
+            if (result)
+            {
+                string enableIOName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{CA_PUMP_ENABLE}");
+                return BeckhoffIOManager.Instance.WriteIoValue(enableIOName, true);
+            }
+            else
+            {
+                return false;
+            }
+        }
+        /// <summary>
+        /// 阴极Pump Off
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool CAPumpOff(string cmd, object[] args)
+        {
+            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{CA_PUMP_ENABLE}");
+            return BeckhoffIOManager.Instance.WriteIoValue(ioName, false);
+        }
+
+        #endregion
+
+        #region Return Valve
+        /// <summary>
+        /// Return Valve
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool ReturnValvePercentOperation(string cmd, object[] args)
+        {
+            if (double.TryParse(args[0].ToString(), out double percent))
+            {
+                _returnValvePercent = percent;
+                return CAPumpSpeed(_caPumpSpeed);
+            }
+            else
+            {
+                LOG.WriteLog(eEvent.ERR_METAL, Module, $"{args[0]} is nor invalid speed");
+                return false;
+            }
+        }
+        /// <summary>
+        /// 设置比例
+        /// </summary>
+        /// <param name="caPumpSpeed"></param>
+        /// <returns></returns>
+        public bool ReturnValvePercent(double percent)
+        {
+            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{RETURN_VALVE_PERCENT}");
+            return BeckhoffIOManager.Instance.WriteIoValue(ioName, percent);
+        }
+        /// <summary>
+        /// Return Valve On
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool ReturnValveOn(string cmd, object[] args)
+        {
+            double percent = SC.GetValue<double>("Reservoir.ReturnOpenDefaultPercentage");
+            bool result = ReturnValvePercent(percent);
+            if (result)
+            {
+                string enableIOName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{RETURN_VALVE}");
+                return BeckhoffIOManager.Instance.WriteIoValue(enableIOName, true);
+            }
+            else
+            {
+                return false;
+            }
+        }
+        /// <summary>
+        /// Return Valve Off
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool ReturnValveOff(string cmd, object[] args)
+        {
+            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{RETURN_VALVE}");
+            return BeckhoffIOManager.Instance.WriteIoValue(ioName, false);
+        }
+
+        #endregion
+
+        #region CA DiReplen
+        /// <summary>
+        /// 阴极DI Replen On
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool CADiReplenOnOperation(string cmd, object[] args)
+        {
+            return CADiReplenOn();
+        }
+        /// <summary>
+        /// 阴极DI Replen On
+        /// </summary>
+        /// <param name="showError"></param>
+        /// <returns></returns>
+        private bool CADiReplenOn()
+        {
+            bool preCondition = CheckPreDiReplenCondition();
+            if (!preCondition)
+            {
+                return false;
+            }
+            if (IsCAHighLevel)
+            {
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"CAHighLevel is activate,Can't do CA_DIReple");
+                return false;
+            }
+            if (IsCALowLevel)
+            {
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"CALowLevel is activate,Can't do CA_DIReple");
+                return false;
+            }
+            if (ReservoirData.AnDiReplen)
+            {
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, "ANDiReplen is on");
+                return false;
+            }
+            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{CA_DI_REPLEN}");
+            return BeckhoffIOManager.Instance.WriteIoValue(ioName, true);
+        }
+
+        /// <summary>
+        /// 阴极DI Replen Off
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool CADiReplenOff(string cmd, object[] args)
+        {
+            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{CA_DI_REPLEN}");
+            bool result = BeckhoffIOManager.Instance.WriteIoValue(ioName, false);
+            if (result)
+            {
+                _persistentValue.IsDiReplenOn = false;
+                if (_currentDireplenOperation == DiReplenOperation.ManualCADiReplen || _currentDireplenOperation == DiReplenOperation.AutoCADiReplen)
+                {
+                    _currentDireplenOperation = DiReplenOperation.None;
+                    _persistentValue.LastTotalReplen = _persistentValue.TotalReplen;
+                    ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
+                }
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// 检验DiReplen前置条件
+        /// </summary>
+        /// <returns></returns>
+        public bool CheckPreDiReplenCondition()
+        {
+            if (!CheckFacilitiesDiReplenStatus())
+            {
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, "Facilities DiReplen is Off, can't start auto diReplen");
+                return false;
+            }
+            SafetyDevice safetyDevice = DEVICE.GetDevice<SafetyDevice>("Safety");
+            if (safetyDevice != null && safetyDevice.SafetyData.Reservoir1CALevelHigh)
+            {
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"Safety high is Activate, can't start auto diReplen");
+                return false;
+            }
+            if (CheckOtherReservoirDiReplenStatus())
+            {
+                return false;
+            }
+            return true;
+        }
+        /// <summary>
+        /// 检验总Di有没有开
+        /// </summary>
+        /// <returns></returns>
+        private bool CheckFacilitiesDiReplenStatus()
+        {
+            SystemFacilities systemFacilities = DEVICE.GetDevice<SystemFacilities>("System.Facilities");
+            if (systemFacilities != null)
+            {
+                return systemFacilities.DIReplenEnable;
+            }
+            return false;
+        }
+        /// <summary>
+        /// 检验是否其他Reservoir Direplen已经
+        /// </summary>
+        /// <returns></returns>
+        protected bool CheckOtherReservoirDiReplenStatus()
+        {
+            List<string> reservoirs = ReservoirItemManager.Instance.InstalledModules;
+            foreach (string item in reservoirs)
+            {
+                if (item != Module)
+                {
+                    ReservoirDevice tmpDevice = DEVICE.GetDevice<ReservoirDevice>(item);
+                    if (tmpDevice.ReservoirData.CaDiReplen)
+                    {
+                        return true;
+                    }
+                    ReservoirItem reservoirItem = ReservoirItemManager.Instance.GetReservoirItem(item);
+                    if (reservoirItem.SubType == ReservoirType.DegasMembrance.ToString())
+                    {
+                        if (tmpDevice.ReservoirData.AnDiReplen)
+                        {
+                            return true;
+                        }
+                    }
+                }
+            }
+            return false;
+        }
+        #endregion
+
+        #region DiReplen Operation
+        /// <summary>
+        /// 重置时长
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="objs"></param>
+        /// <returns></returns>
+        private bool ResetTotalTime(string cmd, object[] objs)
+        {
+            _isDIReplenMaxTimeOut = false;
+            _persistentValue.TotalReplen = 0;
+            _persistentValue.LastTotalReplen = 0;
+            ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
+            return true;
+        }
+        /// <summary>
+        /// 手动阴极注水
+        /// </summary>
+        /// <param name="cmd"></param>
+        /// <param name="args"></param>
+        /// <returns></returns>
+        private bool ManualCADiReplen(string cmd, object[] args)
+        {
+            return ManualDiReplen(CADiReplenOnOperation, DiReplenOperation.ManualCADiReplen, args[0].ToString());
+        }
+        /// <summary>
+        /// 手动注水
+        /// </summary>
+        /// <param name="direplenOn"></param>
+        /// <param name="direplenOperation"></param>
+        /// <returns></returns>
+        private bool ManualDiReplen(Func<string, object[], bool> direplenOn, DiReplenOperation direplenOperation, string timeLength)
+        {
+            if (_currentDireplenOperation != DiReplenOperation.None)
+            {
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"current operation is {_currentDireplenOperation},cannot execute {direplenOperation}");
+                return false;
+            }
+            ReservoirEntity reservoirEntity = Singleton<RouteManager>.Instance.GetModule<ReservoirEntity>(Module);
+            if (!reservoirEntity.IsInitialized)
+            {
+                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"{Module} is not initialized. Can't start DiReplen");
+                return false;
+            }
+            if (_isDIReplenMaxTimeOut)
+            {
+                double diValveMaxOnTime = SC.GetValue<double>($"Reservoir.{Module}.DIValveMaxOnTime");
+                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"Direplen time over conifg's DIValveMaxOnTime:{diValveMaxOnTime} min");
+                return false;
+            }
+            if (_isDIReplenPerfillTimeOut)
+            {
+                double diValveMaxOnTimePerFill = SC.GetValue<double>($"Reservoir.{Module}.DIValveMaxOnTimePerFill");
+                LOG.WriteLog(eEvent.WARN_RESERVOIR, Module, $"Direplen time over conifg's DIValveMaxOnTimePerFill:{diValveMaxOnTimePerFill} min");
+                return false;
+            }
+            bool result = direplenOn("", null);
+            if (result)
+            {
+                _currentDireplenOperation = direplenOperation;
+                _persistentValue.DiReplenTime = DateTime.Now;
+                ReservoirsPersistentManager.Instance.UpdatePersistentValue(Module);
+                int.TryParse(timeLength, out _manualReplenSecond);
+            }
+            return result;
+        }
+        /// <summary>
+        /// 阴极自动流水
+        /// </summary>
+        /// <returns></returns>
+        public bool AutoCADiReplen()
+        {
+            if (IsCALowLevel)
+            {
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"CALowLevel is activate,Can't AutoANDireplen");
+                return false;
+            }
+            return AutoDireplen(CADiReplenOn, DiReplenOperation.AutoCADiReplen);
+        }
+
+        /// <summary>
+        /// 自动注水
+        /// </summary>
+        /// <returns></returns>
+        protected bool AutoDireplen(Func<bool> direplenOn, DiReplenOperation reservoirOperation)
+        {
+            if (_currentDireplenOperation != DiReplenOperation.None)
+            {
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"current operation is {_currentDireplenOperation},cannot execute {reservoirOperation}");
+                return false;
+            }
+            if (_resRecipe == null)
+            {
+                LOG.WriteLog(eEvent.ERR_RESERVOIR, Module, $"recipe is null");
+                return false;
+            }
+            bool result = direplenOn();
+            if (result)
+            {
+                _currentDireplenOperation = reservoirOperation;
+                _persistentValue.DiReplenTime = DateTime.Now;
+            }
+            return result;
+        }
+        #endregion
+
+        /// <summary>
+        /// 检验阴极是否highlevel
+        /// </summary>
+        public bool CheckCAHighLevelStatus()
+        {
+            return ReservoirData.CaWaterLevel > SC.GetValue<double>($"Reservoir.{Module}.CAHighLevel") ? true : false;
+        }
+        /// <summary>
+        /// 检验阴极是否lowlevel
+        /// </summary>
+        public bool CheckCALowLevelStatus()
+        {
+            return ReservoirData.CaWaterLevel < SC.GetValue<double>($"Reservoir.{Module}.CALowLevel") ? true : false;
+        }
 
         public void Reset()
         {
@@ -306,5 +959,14 @@ namespace PunkHPX8_RT.Devices.Reservoir
         public void Terminate()
         {
         }
+        protected enum DiReplenOperation
+        {
+            None,
+            ManualANDiReplen,
+            ManualCADiReplen,
+            AutoANDiReplen,
+            AutoCADiReplen
+        }
+
     }
 }

+ 176 - 0
PunkHPX8_RT/Devices/Reservoir/ReservoirDiReplenHelper.cs

@@ -0,0 +1,176 @@
+using Aitex.Core.RT.Device;
+using Aitex.Core.RT.Log;
+using Aitex.Core.RT.SCCore;
+using MECF.Framework.Common.Alarm;
+using MECF.Framework.Common.Persistent.Reservoirs;
+using PunkHPX8_RT.Devices.Facilities;
+using System;
+
+namespace PunkHPX8_RT.Devices.Reservoir
+{
+    public class ReservoirDiReplenHelper
+    {
+        #region 内部变量
+        /// <summary>
+        /// 模块名称
+        /// </summary>
+        private string _module;
+        /// <summary>
+        /// 持久化对象
+        /// </summary>
+        private ReservoirsPersistentValue _persistentValue;
+        /// <summary>
+        /// 锁
+        /// </summary>
+        private object _locker = new object();
+        #endregion
+        /// <summary>
+        /// 构造函数
+        /// </summary>
+        /// <param name="module"></param>
+        /// <param name="persistentValue"></param>
+        /// <param name="resRecipe"></param>
+        public ReservoirDiReplenHelper(string module, ReservoirsPersistentValue persistentValue)
+        {
+            _module = module;
+            _persistentValue = persistentValue;
+        }
+        /// <summary>
+        /// 监控
+        /// </summary>
+        public void MonitorPeriodTime(ref bool maxTimeout)
+        {
+            double levelHysteresis = SC.GetValue<double>("Reservoir.LevelHysteresis");
+            double diValveMaxOnTimePeriod = SC.GetValue<double>($"Reservoir.{_module}.DIValveMaxOnTimePeriod");
+            //没有在注水
+            if (_persistentValue != null && !_persistentValue.IsDiReplenOn)
+            {
+                //超过时间
+                if (DateTime.Now.Subtract(_persistentValue.PeriodStartTime).TotalMinutes >= diValveMaxOnTimePeriod * 60)
+                {
+                    maxTimeout = false;
+                    _persistentValue.PeriodStartTime = DateTime.Now;
+                    _persistentValue.TotalReplen = 0;
+                    _persistentValue.LastTotalReplen = 0;
+                    ReservoirsPersistentManager.Instance.UpdatePersistentValue(_module);
+                }
+            }
+        }
+        /// <summary>
+        /// 监控手动注水
+        /// </summary>
+        public bool MonitorManualDiReplenComplete(int replenSecond, Func<string, object[], bool> direplenOffAction, ref bool maxTimeout)
+        {
+            lock (_locker)
+            {
+                _persistentValue.TotalReplen = _persistentValue.LastTotalReplen + (int)DateTime.Now.Subtract(_persistentValue.DiReplenTime).TotalSeconds;
+            }
+            //周期内累计补水超时
+            double diValveMaxOnTime = SC.GetValue<double>($"Reservoir.{_module}.DIValveMaxOnTime");
+            if (_persistentValue.TotalReplen >= diValveMaxOnTime * 60)
+            {
+                bool result = direplenOffAction("", null);
+                if (result)
+                {
+                    maxTimeout = true;
+                    LOG.WriteLog(eEvent.WARN_RESERVOIR, _module, $"DiReplen time over conifg's DIValveMaxOnTime:{diValveMaxOnTime} min");
+                    _persistentValue.LastTotalReplen = _persistentValue.TotalReplen;
+                    _persistentValue.IsDiReplenOn = false;
+                    ReservoirsPersistentManager.Instance.UpdatePersistentValue(_module);
+                }
+                return result;
+            }
+            if (DateTime.Now.Subtract(_persistentValue.DiReplenTime).TotalSeconds >= replenSecond)
+            {
+                bool result = direplenOffAction("", null);
+                if (result)
+                {
+                    _persistentValue.LastTotalReplen = _persistentValue.TotalReplen;
+                    _persistentValue.IsDiReplenOn = false;
+                    ReservoirsPersistentManager.Instance.UpdatePersistentValue(_module);
+                    LOG.WriteLog(eEvent.INFO_RESERVOIR, _module, "Manual DiReplen complete");
+                }
+                return result;
+            }
+            return false;
+        }
+        /// <summary>
+        /// 单次自动注水超时
+        /// </summary>
+        /// <returns></returns>
+        public bool AutoDiReplenMonitorTimeOut(Func<string, object[], bool> direplenOffAction, ref bool maxTimeOut, ref bool perfillTimeOut)
+        {
+            lock (_locker)
+            {
+                _persistentValue.TotalReplen = _persistentValue.LastTotalReplen + (int)DateTime.Now.Subtract(_persistentValue.DiReplenTime).TotalSeconds;
+            }
+            //单次补水超时
+            double diValveMaxOnTimePerFill = SC.GetValue<double>($"Reservoir.{_module}.DIValveMaxOnTimePerFill");
+            if (DateTime.Now.Subtract(_persistentValue.DiReplenTime).TotalSeconds >= diValveMaxOnTimePerFill * 60)
+            {
+                bool result = direplenOffAction("", null);
+                if (result)
+                {
+                    perfillTimeOut = true;
+                    LOG.WriteLog(eEvent.WARN_RESERVOIR, _module, $"DiReplen time over conifg's DIValveMaxOnTimePerFill:{diValveMaxOnTimePerFill} min");
+                    AlarmListManager.Instance.AddWarn(_module, $"", $"{_module} DiReplen time over config DIValveMaxOnTimePerFill:{diValveMaxOnTimePerFill} min");
+                    //补水超时关闭总的补水阀
+                    SystemFacilities systemFacilities = DEVICE.GetDevice<SystemFacilities>("System.Facilities");
+                    if (systemFacilities != null)
+                    {
+                        if (systemFacilities.DIReplenEnable) systemFacilities.DiReplenDisableOperation("DiReplenDisableOperation", null);
+                    }
+                    _persistentValue.LastTotalReplen = _persistentValue.TotalReplen;
+                    _persistentValue.IsDiReplenOn = false;
+                    ReservoirsPersistentManager.Instance.UpdatePersistentValue(_module);
+                }
+                return result;
+            }
+            //周期内累计补水超时
+            double diValveMaxOnTime = SC.GetValue<double>($"Reservoir.{_module}.DIValveMaxOnTime");
+            if (_persistentValue.TotalReplen >= diValveMaxOnTime * 60)
+            {
+                bool result = direplenOffAction("", null);
+                if (result)
+                {
+                    maxTimeOut = true;
+                    AlarmListManager.Instance.AddWarn(_module, $"", $"{_module} DiReplen time over config DIValveMaxOnTime:{diValveMaxOnTime} min");
+                    LOG.WriteLog(eEvent.WARN_RESERVOIR, _module, $"DiReplen time over config DIValveMaxOnTime:{diValveMaxOnTime} min");
+                    _persistentValue.LastTotalReplen = _persistentValue.TotalReplen;
+                    _persistentValue.IsDiReplenOn = false;
+                    ReservoirsPersistentManager.Instance.UpdatePersistentValue(_module);
+                }
+                return result;
+            }
+
+            return false;
+        }
+        /// <summary>
+        /// 自动注水是否结束
+        /// </summary>
+        /// <param name="level"></param>
+        /// <param name="recipeLevel"></param>
+        /// <param name="direplenOffAction"></param>
+        /// <returns></returns>
+        public bool AutoDiReplenMonitorComplete(double level, double recipeLevel, bool replenEnable,
+            int direplenTimeRate, int direplenCurrentRate, Func<string, object[], bool> direplenOffAction)
+        {
+            double levelHysteresis = SC.GetValue<double>("Reservoir.LevelHysteresis");
+            //按液位补水
+            if (replenEnable && direplenTimeRate == 0 && direplenCurrentRate == 0 &&
+                level >= recipeLevel)
+            {
+                LOG.WriteLog(eEvent.INFO_RESERVOIR, _module, "Auto DiReplen complete");
+                bool result = direplenOffAction("", null);
+                if (result)
+                {
+                    _persistentValue.LastTotalReplen = _persistentValue.TotalReplen;
+                    _persistentValue.IsDiReplenOn = false;
+                    ReservoirsPersistentManager.Instance.UpdatePersistentValue(_module);
+                    return result;
+                }
+            }
+            return false;
+        }
+    }
+}

+ 8 - 9
PunkHPX8_RT/Devices/Temperature/TemperatureController.cs

@@ -19,6 +19,7 @@ using System.Linq;
 using System.Reflection;
 using System.Text;
 using System.Threading.Tasks;
+using PunkHPX8_Core;
 
 namespace PunkHPX8_RT.Devices.Temperature
 {
@@ -41,8 +42,6 @@ namespace PunkHPX8_RT.Devices.Temperature
         private const string OUT_PUT_RATIO="OutputRatio";
         private const string TEMPERATURE_DATA = "TemperatureData";
         private const string IS_CONNECTED = "IsConnected";
-        private const int ENABLE = 5;
-        private const int DISABLE = 0;
         private const string PERSISTENT_VALUE = "PersistentValue";
         #endregion
 
@@ -144,7 +143,7 @@ namespace PunkHPX8_RT.Devices.Temperature
             if (param.Length == 3 && double.TryParse(param[0].ToString(), out double targetTemperature) && double.TryParse(param[1].ToString(), out double targetTemperatureLowLimit) && double.TryParse(param[2].ToString(), out double targetTemperatureHighLimit))
             {
                 TemperatureConfigManager.Instance.SetTargetTemperature(Module, _address, targetTemperature);
-                if (TemperatureData.ControlOperationModel == ENABLE)
+                if (TemperatureData.ControlOperationModel == (int)TemperatureEnumData.ENABLE)
                 {
                     _startMonitorData = true;
                 }
@@ -155,7 +154,7 @@ namespace PunkHPX8_RT.Devices.Temperature
             if (param.Length == 1 && double.TryParse(param[0].ToString(), out double targetTemperature1))
             {
                 TemperatureConfigManager.Instance.SetTargetTemperature(Module, _address, targetTemperature1);
-                if (TemperatureData.ControlOperationModel == ENABLE)
+                if (TemperatureData.ControlOperationModel == (int)TemperatureEnumData.ENABLE)
                 {
                     _startMonitorData = true;
                 }
@@ -196,8 +195,8 @@ namespace PunkHPX8_RT.Devices.Temperature
                     return false;
                 }
             }
-            TemperatureData.ControlOperationModel = ENABLE;
-            bool result= TemperatureConfigManager.Instance.EnableControl(Module, _address,ENABLE);
+            TemperatureData.ControlOperationModel = (int)TemperatureEnumData.ENABLE;
+            bool result= TemperatureConfigManager.Instance.EnableControl(Module, _address, (int)TemperatureEnumData.ENABLE);
             if (result)
             {
                 LOG.WriteLog(eEvent.INFO_TEMPERATURE, Module, "control operation set enable");
@@ -214,8 +213,8 @@ namespace PunkHPX8_RT.Devices.Temperature
         public bool DisableOperation(string cmd, object[] param)
         {
             _isApplying = false;
-            TemperatureData.ControlOperationModel = DISABLE;
-            bool result= TemperatureConfigManager.Instance.DisableController(Module, _address,DISABLE);
+            TemperatureData.ControlOperationModel = (int)TemperatureEnumData.DISABLE;
+            bool result= TemperatureConfigManager.Instance.DisableController(Module, _address, (int)TemperatureEnumData.DISABLE);
             if(result)
             {
                 if (result)
@@ -494,7 +493,7 @@ namespace PunkHPX8_RT.Devices.Temperature
         public bool SetEnableTargetTemperature(double targetTemperature)
         {
             TemperatureConfigManager.Instance.SetTargetTemperature(Module, _address, targetTemperature);
-            if (_temperatureData.ControlOperationModel == DISABLE)
+            if (_temperatureData.ControlOperationModel == (int)TemperatureEnumData.DISABLE)
             {
                 EnableOperation("", null);
             }

+ 41 - 0
PunkHPX8_RT/Modules/Reservoir/ReservoirEntity.cs

@@ -1,9 +1,13 @@
 using Aitex.Core.RT.Fsm;
 using Aitex.Core.RT.Log;
+using Aitex.Core.RT.RecipeCenter;
 using Aitex.Core.Utilities;
 using MECF.Framework.Common.Equipment;
 using MECF.Framework.Common.Persistent.Reservoirs;
 using MECF.Framework.Common.Persistent.Temperature;
+using MECF.Framework.Common.ProcessCell;
+using MECF.Framework.Common.RecipeCenter;
+using MECF.Framework.Common.ToolLayout;
 using PunkHPX8_Core;
 using System;
 using System.Collections.Generic;
@@ -29,6 +33,10 @@ namespace PunkHPX8_RT.Modules.Reservoir
         /// 持久化数值
         /// </summary>
         private ReservoirsPersistentValue _persistentValue;
+        /// <summary>
+        /// 当前recipe
+        /// </summary>
+        private ResRecipe _currentRecipe;
         #endregion
         #region 属性
         /// <summary>
@@ -66,6 +74,14 @@ namespace PunkHPX8_RT.Modules.Reservoir
         {
             get { return fsm.State == (int)ReservoirState.Initializing; }
         }
+
+        /// <summary>
+        /// 化学液
+        /// </summary>
+        public string Chemistry
+        {
+            get { return CurrentRecipe != null ? CurrentRecipe.Metal : ""; }
+        }
         /// <summary>
         /// 是否禁用
         /// </summary>
@@ -91,6 +107,18 @@ namespace PunkHPX8_RT.Modules.Reservoir
         /// 状态机状态
         /// </summary>
         public ReservoirState State { get { return (ReservoirState)fsm.State; } }
+        /// <summary>
+        /// 是否初始化完成
+        /// </summary>
+        public bool IsInitialized { get { return fsm.State >= (int)ReservoirState.Initialized; } }
+        /// <summary>
+        /// 当前Recipe
+        /// </summary>
+        public ResRecipe CurrentRecipe { get { return LoadCurrentRecipe(); } }
+        /// <summary>
+        /// 用量
+        /// </summary>
+        public ReservoirUsage ReservoirUsage { get { return ReservoirUsageManager.Instance.GetReservoirUsage(Module.ToString()); } }
         #endregion
         /// <summary>
         /// 构造函数
@@ -126,6 +154,19 @@ namespace PunkHPX8_RT.Modules.Reservoir
             }
         }
         /// <summary>
+        /// 加载当前recipe
+        /// </summary>
+        /// <returns></returns>
+        private ResRecipe LoadCurrentRecipe()
+        {
+            if (_persistentValue != null && !string.IsNullOrEmpty(_persistentValue.Recipe))
+            {
+
+                return RecipeFileManager.Instance.LoadGenericityRecipe<ResRecipe>(_persistentValue.Recipe);
+            }
+            return null;
+        }
+        /// <summary>
         /// 初始化Routine
         /// </summary>
         private void InitializeRoutine()

+ 6 - 0
PunkHPX8_RT/PunkHPX8_RT.csproj

@@ -234,10 +234,15 @@
     <Compile Include="Devices\PowerSupplier\PowerSupplierStepRoutine.cs" />
     <Compile Include="Devices\Dose\CrossDoseHelper.cs" />
     <Compile Include="Devices\Dose\DosingSystemHelper.cs" />
+    <Compile Include="Devices\Reservoir\ANPumpOnRoutine.cs" />
     <Compile Include="Devices\Reservoir\DIReservoirDevice.cs" />
     <Compile Include="Devices\Reservoir\DMReservoirDevice.cs" />
     <Compile Include="Devices\Reservoir\HSReservoirDevice.cs" />
+    <Compile Include="Devices\Reservoir\LevelCurveManager.cs" />
+    <Compile Include="Devices\Reservoir\ReservoirANPumpSpeedHelper.cs" />
+    <Compile Include="Devices\Reservoir\ReservoirCAPumpSpeedHelper.cs" />
     <Compile Include="Devices\Reservoir\ReservoirDevice.cs" />
+    <Compile Include="Devices\Reservoir\ReservoirDiReplenHelper.cs" />
     <Compile Include="Devices\Safety\SafetyAllOnRoutine.cs" />
     <Compile Include="Devices\SRD\SrdCommonChuckATMRoutine.cs" />
     <Compile Include="Devices\SRD\SrdCommonFlipperRoutine.cs" />
@@ -287,6 +292,7 @@
     <Compile Include="Modules\LPs\LoadPortMapRoutine.cs" />
     <Compile Include="Modules\LPs\LoadPortUnDockRoutine.cs" />
     <Compile Include="Modules\ModuleHomeAllRoutine.cs" />
+    <Compile Include="Devices\Reservoir\CAPumpOnRoutine.cs" />
     <Compile Include="Modules\Reservoir\ReservoirEntity.cs" />
     <Compile Include="Modules\Reservoir\ReservoirMsg.cs" />
     <Compile Include="Modules\SRD\SRDAWCCycleRoutine.cs" />