using Aitex.Core.RT.Device;
using Aitex.Core.RT.Device.Devices;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.Routine;
using Aitex.Core.RT.SCCore;
using MECF.Framework.Common.DBCore;
using MECF.Framework.Common.Equipment;
using MECF.Framework.RT.EquipmentLibrary.HardwareUnits.MFCs;
using SicModules.PMs.Routines.Base;
using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace SicModules.PMs.Routines
{
    public class PMLeakCheckRoutine : PMBaseRoutine
    {
        private enum RoutineStep
        {
            RotationEnable,
            HeatEnable,
            VentPumpClose,
            ArSupply,

            SetGroupA,
            SetGroupB,
            SetGroupC,
            SetGroupE,
            SetGroupF,
            SetGroupH,
            SetGroupD,
            SetGroupG,
            SetGroupK,

            SetMFCMode,
            SetPCMode,

            SetM1toM17,
            SetPC,
            SetGroupJ,
            SetM291519to38,

            SetGasOut,
            SetTv,

            SetGasIn1,      //V68
            OpenFinal1,     //V91-96
            SetMfcFinal1,   //M32-38
            SetMfc19to26,

            SetTVCloseMode,
            SetTvPositionToZero,

            SetGasIn2,
            SetGroupJOpen,
            SetM18toM40,
            SetM17M18,

            OpenFinal2,
            CloseFinal2,
            SetMfcFinal2,

            SetEPV1,
            SetEPV2,
            SetTvMode,
            SetTVEnable,
            SetTVPressMode,
            SetTv1,
            WaitTv1,
            Set0M18toM40,
            SetM18,
            SetTVPositionMode,
            SetTVPositionMode_1,
            SetTVPositionMax,
            SetTVPositionMax_1,
            SetTVPressMode1,
            SetM2TOM40Default,
            SetM2toM40_1,
            SetM18Default,
            SetM18_1,

            SetTvOpen,
            SetTvModeToPress,
            SetTVtoCurrent,
            RoutinePre,

            SetPressureUp,
            SetPressureDown,
            WaitPressureDown,
            WaitPressureUp,
            VentPumpOpen,

            PressureToMax,
            PressureToMin,

            StartLoop,
            EndLoop,

            SetMfc28to31,
            SetReactorPress,
            CloseFinal1,
            CloseFinanl2,
            CloseEPV2,
            SetTVCloseMode2,
            CLoseTvvvv,
            SetEPVVV2,
            CLoseEPV1,
            CheckPM1000,
            WaitPressUpTo1000,
            WaitPressUpTo1020,

            SetPressUpOrDown1,
            SetPressUpOrDown2,
            SetMfc28to40Special,
            WaitPmPressureUp,
            SetMfc28to40Default,

            SetV75,
            SetV76,
            SetV751,
            SetV761,

            SetPC567Close,
            SetPC567Mode,
            SetPC567Default,
            OpenEPV2,
            WaitPressUpTo20,
            SetMfc291519to38,
            WaitPressUpTo0,
            SetMfc291519to38ToDefault,
            WaitPressDownTo20,
            SetPmPressureTo20,
            CloseMfcFinal1,
            CloseMfcFinal2,
            CloseMfc2915,
            CheckFinal1Open,
            DoLeakCheck,
            CalcLeakCheck,
            SetTVClose,
            CloseTV,
            CloseV27,
            CloseV28,
            CloseV69,
            CloseV70,
            SetGroupV25,
            SetV31,
            SetV32,
            SetV35V36,
            CloseV68,

            SetV92V93V95,
            SetV94,
            SetV96,
            SetV68,
            SetM32M35M37,
            SetM36,
            SetM38,

            CloseM32M35M37,
            CloseM36,
            CloseM38,
            SetV103,
            SetV105,
            CloseGasIn1,

            SetTVCloseMode1,
            CLoseTV1,
            SetEPV2_1,
            SetEPV2_2,
            SetTVEnable_2,

            TimeDelay1,
            TimeDelay2,
            TimeDelay3,
            TimeDelay4,
            TimeDelay5,
            TimeDelay6,
            TimeDelay7,
            TimeDelay8,
            TimeDelay9,
            TimeDelay10,
            TimeDelay11,
            TimeDelay12,
            TimeDelay13,
            TimeDelay14,
            TimeDelay15,
            TimeDelay16,
            TimeDelay17,

            LoopSetTVPressMode1, 
            LoopSetM18toM40, 
            LoopSetTv1, 
            LoopSet0M18toM40, 
            LoopSetM18Default, 
            LoopWaitTv1
        }

        enum LeakCheckType
        {
            ChamberOnly,

            ChamberAndGasline,

            ChamberAndGaslineToFacility,
        }

        private ModuleName moduleName;

        private IoThrottleValve2 _IoThrottle;

        private int _rotationCheckSpeed = 0; //设置旋转速度为0后检查是否转速低于此数值
        private int _rotationCloseTimeout;   //旋转停止超时
        private int _heatTimeOut = 5;             //Heat关闭等待Di反馈超时时间

        private double _pressureMax;            //Loop压力上限可设置600-800）
        private double _pressureMin;            //Loop压力下限可设置  0-400）
        private int _pressureLoopCount;         //Loop次数
        private int _pressureMinDelay;
        private int _pressureMaxDelay;
        private int _pressureMinTimeout;
        private int _pressureMaxTimeout;

        private int _IoValueOpenCloseTimeout = 10;   //开关阀门超时时间
        private double _pmPressureMaxDiff;          //蝶阀与目标压力的差值范围(认为调整到位了)
        private int _throttleTimeout;               //蝶阀调整到指定压力的超时时间

        private int _paramContinuePumpTime;
        private int _paramLeakCheckTime;
        private double _beginPressure;
        private double _beginTime;
        private int _calTimes;              //第几次计算LeakCheck的值（每60秒计算一次）
        private double _leakSpec;
        private int _routineTimeOut;
        //private int _EPV2OpenDelayTime = 9;

        private bool _useSettingValue = false;

        private Stopwatch _swTimer = new Stopwatch();
        public int ElapsedTime
        {
            get { return _swTimer.IsRunning ? (int)(_swTimer.ElapsedMilliseconds / 1000) : 0; }
        }

        public PMLeakCheckRoutine(ModuleName module, PMModule pm1) : base(module, pm1)
        {
            Module = module.ToString();
            Name = "Leak Check";

            _IoThrottle = DEVICE.GetDevice<IoThrottleValve2>($"{Module}.TV");
        }

        internal void Init(int pumpTime, int leakCheckTime)
        {
            _paramContinuePumpTime = pumpTime;
            _paramLeakCheckTime = leakCheckTime;
            _useSettingValue = true;
        }

        internal void Init()
        {
            _useSettingValue = false;
        }

        public override Result Start(params object[] objs)
        {
            Reset();

            _swTimer.Restart();


            _rotationCloseTimeout = SC.GetValue<int>($"PM.{Module}.RotationCloseTimeout");
            _pmPressureMaxDiff = SC.GetValue<double>($"PM.{Module}.ThrottlePressureMaxDiff");
            _throttleTimeout = SC.GetValue<int>($"PM.{Module}.ThrottlePressureTimeout");
            _rotationCloseTimeout = SC.GetValue<int>($"PM.{Module}.RotationCloseTimeout");

            _pressureLoopCount = SC.GetValue<int>($"PM.{Module}.LeakCheck.CyclePurgeCount");
            _pressureMin = SC.GetValue<double>($"PM.{Module}.LeakCheck.PumpBasePressure");
            _pressureMinDelay = SC.GetValue<int>($"PM.{Module}.LeakCheck.PumpDelayTime");
            _pressureMinTimeout = SC.GetValue<int>($"PM.{Module}.LeakCheck.PumpTimeout");
            _pressureMax = SC.GetValue<double>($"PM.{Module}.LeakCheck.VentBasePressure");
            _pressureMaxDelay = SC.GetValue<int>($"PM.{Module}.LeakCheck.VentDelayTime");
            _pressureMaxTimeout = SC.GetValue<int>($"PM.{Module}.LeakCheck.VentTimeout");
            _routineTimeOut = SC.GetValue<int>($"PM.{Module}.LeakCheck.RoutineTimeOut");
            //_EPV2OpenDelayTime = SC.GetValue<int>($"PM.{Module}.TimeDelayAlterEPV2Open");

            if (!_useSettingValue)
            {
                _paramContinuePumpTime = SC.GetValue<int>($"PM.{Module}.LeakCheck.ContinuePumpTime");
                _paramLeakCheckTime = SC.GetValue<int>($"PM.{Module}.LeakCheck.LeakCheckDelayTime");
            }

            _leakSpec = SC.GetValue<double>($"PM.{Module}.LeakCheck.LeakSpec");

            if (!PmDevice.SetIOValueByGroup(IoGroupName.V70, false))
            {
                EV.PostAlarmLog(Module, "Can not close V70-1");
                return Result.FAIL;
            }

            Notify($"Start   LoopCount:{_pressureLoopCount}");
            return Result.RUN;
        }

        public override Result Monitor()
        {
            try
            {
                CheckRoutineTimeOut();

                //关闭V76,打开V75
                SetIoValueByGroup((int)RoutineStep.SetV76, IoGroupName.V76, false, _IoValueOpenCloseTimeout);
                SetIoValueByGroup((int)RoutineStep.SetV75, IoGroupName.V75, true, _IoValueOpenCloseTimeout);

                //旋转停止,加热停止
                SetHeatEnable((int)RoutineStep.HeatEnable, false, _heatTimeOut);
                SetRotationValve((int)RoutineStep.RotationEnable, _rotationCheckSpeed, false, _rotationCloseTimeout);

                //3、 关闭 V72(GasBox.Vent.Pump） 。
                SetIoValueByGroup((int)RoutineStep.VentPumpClose, IoGroupName.VentPump, false, _IoValueOpenCloseTimeout);

                //4、 打开 V31(H2.Supply)、 V32(N2Supply)。
                SetIoValueByGroup((int)RoutineStep.SetV31, IoGroupName.V31, true, _IoValueOpenCloseTimeout);
                SetIoValueByGroup((int)RoutineStep.SetV32, IoGroupName.V32, true, _IoValueOpenCloseTimeout);

                //5、 打开 V35(H2N2.Line1)、 V36(H2N2.Line2)，关闭 B/C/E/F/H/KValves,打开 D/G Valves； 打开 J valve。
                SetIoValueByGroup((int)RoutineStep.SetV35V36, IoGroupName.V35V36, true, _IoValueOpenCloseTimeout);
                SetIoValueByGroup((int)RoutineStep.SetGroupB, IoGroupName.B, false, _IoValueOpenCloseTimeout);
                SetIoValueByGroup((int)RoutineStep.SetGroupC, IoGroupName.C, false, _IoValueOpenCloseTimeout);
                SetIoValueByGroup((int)RoutineStep.SetGroupE, IoGroupName.E, false, _IoValueOpenCloseTimeout);
                SetIoValueByGroup((int)RoutineStep.SetGroupF, IoGroupName.F, false, _IoValueOpenCloseTimeout);
                SetIoValueByGroup((int)RoutineStep.SetGroupH, IoGroupName.H, false, _IoValueOpenCloseTimeout);
                SetIoValueByGroup((int)RoutineStep.SetGroupK, IoGroupName.K, false, _IoValueOpenCloseTimeout);
                SetIoValueByGroup((int)RoutineStep.SetGroupD, IoGroupName.D, true, _IoValueOpenCloseTimeout);
                SetIoValueByGroup((int)RoutineStep.SetGroupG, IoGroupName.G, true, _IoValueOpenCloseTimeout);
                SetIoValueByGroup((int)RoutineStep.SetGroupJ, IoGroupName.J, true, _IoValueOpenCloseTimeout);

                //6、 M1-M17 设定为 default 值，所有 PC 设定为 default 值(PC1-PC9)， M18-M40（除M30\M34\M39）） MFC 3s ramp 到 default 值。
                SetMfcToDefaultByGroup((int)RoutineStep.SetM1toM17, MfcGroupName.M1toM17, 0);
                SetPcToDefault((int)RoutineStep.SetPC, _lstPcList);
                SetMfcToDefaultByGroup((int)RoutineStep.SetM18toM40, MfcGroupName.M18toM40No303439, 3);

                //7、打开 EPV2，等待 3s, 打开 TV enable, 压力模式，伺服压力默认设定为 0mbar；
                SetIoValueByGroup((int)RoutineStep.SetEPV2, IoGroupName.EPV2, true, _IoValueOpenCloseTimeout);
                TimeDelay((int)RoutineStep.TimeDelay1, 3);
                SetThrottleEnableAndWait((int)RoutineStep.SetTVEnable, _IoThrottle, 5);
                SetThrottleToPressModeAndWait((int)RoutineStep.SetTVPressMode, _IoThrottle, 5);
                SetThrottleToTargetAndNoWait((int)RoutineStep.SetTv1, _IoThrottle, _pressureMin);

                //8、当蝶阀设定值 ramp 到 0 后，将 M18-M40（除 M30\M34\M39）） MFC 3s ramp 到 0；
                SetMfcByGroup((int)RoutineStep.Set0M18toM40, MfcGroupName.M18toM40No303439, 0, 3);

                //9、等待腔体压力到 Pump Base Pressure（可配置），蝶阀改为位置模式，开度设定 100%
                WaitChamberPressDownTo((int)RoutineStep.WaitTv1, _pressureMin, _pressureMinTimeout);
                TimeDelay((int)RoutineStep.TimeDelay8, 3);
                SetThrottleToPositionMode((int)RoutineStep.SetTVPositionMode, _IoThrottle, _throttleTimeout);
                TimeDelay((int)RoutineStep.TimeDelay11, 3);
                SetThrottleSetPosition((int)RoutineStep.SetTVPositionMax, _IoThrottle, 100, _throttleTimeout);
                TimeDelay((int)RoutineStep.TimeDelay9, 3);
                
                //循环开始
                Loop((int)RoutineStep.StartLoop, _pressureLoopCount);
                //蝶阀改为压力模式
                //M18 - M40（除 M30\M34\M39）） MFC 3s ramp 到 default 值。
                //压力 servo 到 300（可配置）
                //压力达到后， M18 - M40（除 M30\M34\M39）） MFC 5s ramp 到 0。
                //等待压力到达 0（可配置）
                SetThrottleToPressModeAndWait((int)RoutineStep.LoopSetTVPressMode1, _IoThrottle, 5);
                SetMfcToDefaultByGroup((int)RoutineStep.LoopSetM18toM40, MfcGroupName.M18toM40No303439, 3);
                SetThrottleToTargetAndNoWait((int)RoutineStep.LoopSetTv1, _IoThrottle, _pressureMin);
                SetMfcByGroup((int)RoutineStep.LoopSet0M18toM40, MfcGroupName.M18toM40No303439, 0, 5);
                SetMfcToDefaultByGroup((int)RoutineStep.LoopSetM18Default, MfcGroupName.M18, 3);
                WaitChamberPressDownTo((int)RoutineStep.LoopWaitTv1, _pressureMin, _pressureMinTimeout); 

                EndLoop((int)RoutineStep.EndLoop);
                //11、 依次关闭 Final valves；
                //先 关 闭 V93 （ ChamberPurge.Final ）、 V94 （ RotationUpPurge.Final ）、 V95（ConfinementRing.Final）、 V96（HeaterWF.Final）；
                //再关闭 V87（ SHPurge.Final）、 V88（ OuterGas.Final）、 V89（ InnerGas.Final）、 V90（ MiddleGas.Final ）、 V91 （ OpticPurge.Final ）、 V92 （ GasRingPurge.Final ）、 V97（CarryGas.Final）；
                SetIoValueByGroup((int)RoutineStep.CloseFinal1, IoGroupName.Final1, false, _IoValueOpenCloseTimeout);
                SetIoValueByGroup((int)RoutineStep.CloseFinal2, IoGroupName.Final2, false, _IoValueOpenCloseTimeout);
                //12、延时一定时间后（参数配置）， 关闭蝶阀，再关闭 EPV2， 开始测漏记录腔体压力 P1（mbar）和时间 T1，测试时间 T（秒） 结束后， 记录腔体压力 P2 和时间 T2
                //计算腔体漏率
                //腔体漏率 =（P2 - P1） /（T2 - T1） *60
                //所得漏率与设定范围对比，小于范围，测漏成功，否则报错提示
                TimeDelay((int)RoutineStep.TimeDelay15, _paramContinuePumpTime);
                SetThrottleToCloseMode((int)RoutineStep.SetTVCloseMode2, _IoThrottle, 8);
                SetThrottleDisable((int)RoutineStep.CLoseTvvvv, _IoThrottle, 8);
                TimeDelay((int)RoutineStep.TimeDelay16, 1);
                SetIoValueByGroup((int)RoutineStep.SetEPVVV2, IoGroupName.EPV2, false, _IoValueOpenCloseTimeout);

                //计算漏率
                DoLeakCheck((int)RoutineStep.DoLeakCheck, _paramLeakCheckTime);
                TimeDelay((int)RoutineStep.TimeDelay17, _paramLeakCheckTime);
                CalcLeakCheck((int)RoutineStep.CalcLeakCheck, _paramLeakCheckTime);
                //CalcLeackCheckPerMinute(_paramLeakCheckTime);
            }
            catch (RoutineBreakException)
            {
                return Result.RUN;
            }
            catch (RoutineFaildException)
            {
                return Result.FAIL;
            }

            _swTimer.Stop();
            return Result.DONE;
        }

        public override void Abort()
        {
            //Stop($"{Module} leak check aborted");
            _swTimer.Stop();
            PmDevice._ioThrottleValve.StopRamp();
            PmDevice.SetMfcStopRamp(PmDevice.GetMfcListByGroupName(MfcGroupName.All));
            PmDevice.SetRotationServo(0, 0);

            base.Abort();
        }

        private void CheckRoutineTimeOut()
        {
            if (_routineTimeOut > 10)
            {
                if ((int)(_swTimer.ElapsedMilliseconds / 1000) > _routineTimeOut)
                {
                    EV.PostAlarmLog(Module, $"Routine TimeOut! over {_routineTimeOut} s");
                    throw (new RoutineFaildException());
                }
            }
        }

        public void DoLeakCheck(int id, double time)
        {
            Tuple<bool, Result> ret = Execute(id, () =>
            {
                Notify($"Keep pressure for {time} seconds");

                _beginTime = _swTimer.ElapsedMilliseconds;
                //_beginPressure = PmDevice.ChamberPressure;
                _beginPressure = PmDevice._ioThrottleValve.PressureFeedback;

                _calTimes = 1;
                return true;
            });
            if (ret.Item1)
            {
                if (ret.Item2 == Result.FAIL)
                {
                    throw (new RoutineFaildException());
                }
                else
                    throw (new RoutineBreakException());
            }
        }

        public void CalcLeackCheckPerMinute(int time)
        {
            //大于LeakCheck时间,
            if ((int)((_swTimer.ElapsedMilliseconds - _beginTime) / 1000) >= time)
            {
                int leakSecond = (int)((_swTimer.ElapsedMilliseconds - _beginTime) / 1000);
                double endPressure = PmDevice.ChamberPressure;
                double leakSpan = endPressure - _beginPressure;
                double leakRate = (endPressure - _beginPressure) / (leakSecond / 60.0);
                if (leakRate > _leakSpec)
                {
                    LeakCheckDataRecorder.Add(time, (int)_beginPressure, (int)endPressure, leakRate, Result.FAIL.ToString(), "", Module);
                    EV.PostInfoLog(Module, $"Leak check result: end at {DateTime.Now.ToString("HH:mm:ss")}, start: {_beginPressure:F2}mbar, end: {endPressure:F2}mbar, using {time} seconds, leak rate: {leakRate:F2}");

                }
                else
                {
                    LeakCheckDataRecorder.Add(time, (int)_beginPressure, (int)endPressure, leakRate, Result.Succeed.ToString(), "", Module);
                    EV.PostInfoLog(Module, $"Leak check result: end at {DateTime.Now.ToString("HH:mm:ss")}, start: {_beginPressure:F2}mbar, end: {endPressure:F2}mbar, using {time} seconds, leak rate: {leakRate:F2}");

                }
            }
            else
            {
                if ((int)((_swTimer.ElapsedMilliseconds - _beginTime) / 1000) >= 60 * _calTimes)
                {
                    _calTimes++;
                    int leakSecond = (int)((_swTimer.ElapsedMilliseconds - _beginTime) / 1000);
                    double endPressure = PmDevice.ChamberPressure;
                    double leakSpan = endPressure - _beginPressure;
                    double leakRate = (endPressure - _beginPressure) / (leakSecond / 60.0);
                    if (leakRate > _leakSpec)
                    {
                        EV.PostInfoLog(Module, $"Leak check Failed Count {_calTimes - 1}: using {leakSecond} seconds, leak rate: {leakRate:F2} ,Rate over {_leakSpec}");

                        throw (new RoutineFaildException());
                    }
                    EV.PostInfoLog(Module, $"Leak check Count {_calTimes - 1}: using {leakSecond} seconds, leak rate: {leakRate:F2}");
                }
                throw (new RoutineBreakException());
            }
        }


        public void CalcLeakCheck(int id, int time)
        {
            Tuple<bool, Result> ret = Execute(id, () =>
            {
                //double endPressure = PmDevice.ChamberPressure;
                double endPressure = PmDevice._ioThrottleValve.PressureFeedback;
                double leakSpan = endPressure - _beginPressure;
                double leakRate = (endPressure - _beginPressure) / (time / 60.0);
                if (leakRate > _leakSpec)
                {
                    LeakCheckDataRecorder.Add(time, (int)_beginPressure, (int)endPressure, leakRate, Result.FAIL.ToString(), "", Module);

                    EV.PostInfoLog(Module, $"Leak check result: end at {DateTime.Now.ToString("HH:mm:ss")}, start: {_beginPressure:F2}mbar, end: {endPressure:F2}mbar, using {time} seconds, leak rate: {leakRate:F2}");
                    return false;
                }
                else
                {
                    LeakCheckDataRecorder.Add(time, (int)_beginPressure, (int)endPressure, leakRate, Result.Succeed.ToString(), "", Module);

                    EV.PostInfoLog(Module, $"Leak check result: end at {DateTime.Now.ToString("HH:mm:ss")}, start: {_beginPressure:F2}mbar, end: {endPressure:F2}mbar, using {time} seconds, leak rate: {leakRate:F2}");
                    return true;
                }
            });
        }
    }
}
