using Aitex.Core.RT.Device;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.Routine;
using Aitex.Core.RT.SCCore;
using CyberX8_Core;
using CyberX8_RT.Devices.Reservoir;
using MECF.Framework.Common.Beckhoff.ModuleIO;
using MECF.Framework.Common.IOCore;
using MECF.Framework.Common.Persistent.Reservoirs;
using MECF.Framework.Common.Routine;
using MECF.Framework.Common.TwinCat;
using System;
namespace CyberX8_RT.Modules.Reservoir
{
    public class ANTransferPumpOnRoutine : RoutineBase, IRoutine
    {
        private enum ANTransferPumpStep
        {
            CrossDoseValveOn,
            Reset,
            WaitReset,
            CheckReadyToEnable,
            Enable,
            CheckReady,
            WriteParameter,
            Execute,
            Wait,
            Done,
            End
        }
        private enum STMStatus
        {
            ReadyToEnable,
            Ready,
            Warning,
            Error
        }
        private enum POSStatus
        {
            Busy,
            InTarget,
            Warning,
            Error
        }
        private enum ENCStatus
        {
            SetCounterDone            
        }
        #region 常量
        private const string TRANSFER_PUMP_ENABLE = "TransferPumpEnable";
        private const string TRANSFER_PUMP_RESET = "TransferPumpReset";
        private const string TRANSFER_PUMP_EXECUTE = "TransferPumpExecute";
        private const string TRANSFER_PUMP_TARGET_POSITION = "TransferPumpTargetPosition";
        private const string TRANSFER_PUMP_SPEED = "TransferPumpSpeed";
        private const string TRANSFER_PUMP_START_TYPE = "TransferPumpStartType";
        private const string TRANSFER_PUMP_ACCELERATION = "TransferPumpAcceleration";
        private const string TRANSFER_PUMP_DECELERATION = "TransferPumpDeceleration";
        private const string CROSS_DOSE_ENABLE = "CrossDoseEnable";
        #endregion
        #region 内部变量
        /// 
        /// Module Name
        /// 
        private string _moduleName;
        /// 
        /// CompactMembranReservoirDevice 
        /// 
        private CompactMembranReservoirDevice _cmReservoirDevice;
        /// 
        /// 加速度
        /// 
        private int _acceleration;
        /// 
        /// 减速度
        /// 
        private int _deceleration;
        /// 
        /// 速度
        /// 
        private int _speed;
        /// 
        /// 目标位置
        /// 
        private double _targetPosition;
        /// 
        /// 开始位置
        /// 
        private double _startPosition;
        /// 
        /// Start Type
        /// 
        private int _startType = 1; //RELATIVE(相对位置)
        /// 
        /// ReservoirsPersistentValue
        /// 
        private ReservoirsPersistentValue _persistentValue;
        /// 
        /// 超时时间
        /// 
        private int _timeOut = 2000;
        /// 
        /// PumpFactor
        /// 
        private double _pumpFactor;
        /// 
        /// 最小流量
        /// 
        private double _flowRateMin;
        /// 
        /// StartUpHoldOffTime    
        /// 
        private int _startUpHoldOffTime;
        /// 
        /// Pump启动时刻
        /// 
        private int _startTime;
        #endregion
        #region 属性
        public RState CurrentState { get { return Runner.Status; } }
        #endregion
        /// 
        /// 构造函数
        /// 
        /// 
        public ANTransferPumpOnRoutine(string module) : base(module)
        {
            _moduleName = module;
        }
        /// 
        /// 中止
        /// 
        public void Abort()
        {
            Runner.Stop("Manual abort");
        }
        /// 
        /// 监控
        /// 
        /// 
        public RState Monitor()
        {
            Runner.Run(ANTransferPumpStep.CrossDoseValveOn, OpenValve, _delay_1ms)
                .Run(ANTransferPumpStep.Reset, ResetCrossDose, _delay_1ms)
                .Wait(ANTransferPumpStep.WaitReset, WaitReset, _timeOut)
                .Run(ANTransferPumpStep.CheckReadyToEnable, CheckReadyToEnable, _delay_1ms)
                .Run(ANTransferPumpStep.Enable, EnableOperation, _delay_1ms)
                .Run(ANTransferPumpStep.CheckReady, CheckReady, _delay_1ms)
                .Run(ANTransferPumpStep.WriteParameter, WriteParameter, _delay_1ms)
                .Run(ANTransferPumpStep.Execute, ExecuteOperation, _delay_1ms)
                .WaitWithStopCondition(ANTransferPumpStep.Wait, WaitCompleteEndStatus, WaitCompleteErrorStatus)
                .Run(ANTransferPumpStep.Done, DoneOperation, _delay_1ms)
                .End(ANTransferPumpStep.End, NullFun);
            return Runner.Status;
        }
        /// 
        /// 启动
        /// 
        /// 
        /// 
        public RState Start(params object[] objs)
        {
            _startType = 2;
            _cmReservoirDevice = DEVICE.GetDevice($"{_moduleName}");
            if (_cmReservoirDevice == null)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, "Compact Membran Reservoir Device is not exist");
                return RState.Failed;
            }
            _persistentValue = ReservoirsPersistentManager.Instance.GetReservoirsPersistentValue(_moduleName);
            if (_persistentValue == null)
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, "Reservoir Persistent Value Object is not exist");
                return RState.Failed;
            }
            //加速度(count/s/s)
            if (SC.ContainsItem($"Reservoir.{Module}.ANTransferPumpAcceleration"))
            {
                _acceleration = SC.GetValue($"Reservoir.{Module}.ANTransferPumpAcceleration");
                if (_acceleration == 0) return RState.Failed;
            }
            //减速度(count/s/s)
            if (SC.ContainsItem($"Reservoir.{Module}.ANTransferPumpDeceleration"))
            {
                _deceleration = SC.GetValue($"Reservoir.{Module}.ANTransferPumpDeceleration");
                if (_deceleration == 0) return RState.Failed;
            }
            //速度(count/s)
            if (SC.ContainsItem($"Reservoir.{Module}.ANTransferPumpSpeed"))
            {
                _speed = SC.GetValue($"Reservoir.{Module}.ANTransferPumpSpeed");
                if (_speed == 0) return RState.Failed;
            }
            //目标位置(count)(1mL = 23809.96272 counts)
            _startPosition = _cmReservoirDevice.ReservoirData.TransferActualPosition;
            _targetPosition = (double)objs[0];
            _pumpFactor = _persistentValue.CrossDosePumpFactor;
            if(_pumpFactor == 0)
            {
                LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, "Pump Factor is zero");
                return RState.Failed;
            }
            //最小FlowRate(mL/min)
            if (SC.ContainsItem($"Reservoir.{Module}.FlowRateMin"))
            {
                _flowRateMin = SC.GetValue($"Reservoir.{Module}.FlowRateMin");
            }
            //StartUpHoldOffTime(流量检测时刻,ms)
            if (SC.ContainsItem($"Reservoir.{Module}.StartUpHoldOffTime"))
            {
                _startUpHoldOffTime = SC.GetValue($"Reservoir.{Module}.StartUpHoldOffTime");
            }
            return Runner.Start(Module, "AN Transfer Pump On");
        }
        /// 
        /// 打开CrossDose Valve
        /// 
        /// 
        private bool OpenValve() 
        {
            if (!_cmReservoirDevice.CrossDoseOn("", null))
            {
                LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, $"Close CrossDose Valve failed");
                return false;
            }
            return true;    
        }
        /// 
        /// Reset CrossDose
        /// 
        /// 
        private bool ResetCrossDose()
        {
            return _cmReservoirDevice.ResetCrossDose("", null);
        }
        /// 
        /// Reset CrossDose
        /// 
        /// 
        private bool WaitReset()
        {
            if (_cmReservoirDevice.ResetCrossDoseMonitor())
            {
                return true;
            }
            return false;
        }
        /// 
        /// 检查电机ReadyToEnable状态
        /// 
        /// 
        private bool CheckReadyToEnable()
        {
            //STM:ReadyToEnable = 1
            ushort status = (ushort)_cmReservoirDevice.ReservoirData.TransferPumpSTMStatus;
            if (!CheckSTMStatus(status, STMStatus.ReadyToEnable))
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, $"STMStatus: {status}. Ready to enable is false");
                return false;
            }
            
            return true;
        }
        /// 
        /// EnableOperation
        /// 
        /// 
        private bool EnableOperation()
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_ENABLE}");
            bool result = IOModuleManager.Instance.WriteIoValue(ioName, true);
            if (!result)
            {
                return false;
            }
            return true;
        }
        /// 
        /// 检查电机Ready状态
        /// 
        /// 
        private bool CheckReady()
        {
            //STM:Ready = 1
            ushort status = (ushort)_cmReservoirDevice.ReservoirData.TransferPumpSTMStatus;
            if (!CheckSTMStatus(status, STMStatus.Ready))
            {
                LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, $"STMStatus: {status}. Ready is false");
                return false;
            }
            return true;
        }
        /// 
        /// 设置电机参数
        /// 
        /// 
        private bool WriteParameter()
        {
            //设置加速度
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_ACCELERATION}");
            bool result = IOModuleManager.Instance.WriteIoValue(ioName, _acceleration);
            if (!result)
            {
                return false;
            }
            //设置减速度
            ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_DECELERATION}");
            result = IOModuleManager.Instance.WriteIoValue(ioName, _deceleration);
            if (!result)
            {
                return false;
            }
            //设置速度
            ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_SPEED}");
            result = IOModuleManager.Instance.WriteIoValue(ioName, _speed);
            if (!result)
            {
                return false;
            }
            //设置StartType
            ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_START_TYPE}");
            result = IOModuleManager.Instance.WriteIoValue(ioName, _startType);
            if (!result)
            {
                return false;
            }
            //设置目标位置(mL)
            ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_TARGET_POSITION}");
            result = IOModuleManager.Instance.WriteIoValue(ioName, _targetPosition/ _pumpFactor);
            if (!result)
            {
                return false;
            }
            return true;
        }
        /// 
        /// 运动到位置
        /// 
        /// 
        private bool ExecuteOperation()
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_EXECUTE}");
            bool result = IOModuleManager.Instance.WriteIoValue(ioName, true);
            if (!result)
            {
                return false;
            }
            _startTime = Environment.TickCount;
            ReservoirsPersistentManager.Instance.UpdatePersistentValue(_moduleName);
            return true;
        }
        
        /// 
        ///  WaitCompleteEnd
        /// 
        /// 
        private bool WaitCompleteEndStatus()
        {
            ushort status = (ushort)_cmReservoirDevice.ReservoirData.TransferPumpPOSStatus;
            double dosedVolume = (_cmReservoirDevice.ReservoirData.TransferActualPosition - _startPosition) * _pumpFactor;
            _cmReservoirDevice.ReservoirData.RemainingCrossDoseVolume = Math.Round(_targetPosition - dosedVolume,2);
            if (CheckPOSStatus(status, POSStatus.InTarget) && !CheckPOSStatus(status, POSStatus.Busy))
            {
                return true;
            }
            return false;
            
        }
        /// 
        ///  WaitCompleteError
        /// 
        /// 
        private bool WaitCompleteErrorStatus()
        {
            ushort status = (ushort)_cmReservoirDevice.ReservoirData.TransferPumpPOSStatus;
            //流量检测
            if(_cmReservoirDevice.ANTransferFlow != null)
            {
                int ticks = Environment.TickCount - _startTime;
                double flow = _cmReservoirDevice.ANTransferFlow.CounterValue;
                if (ticks >= _startUpHoldOffTime && flow < _flowRateMin)
                {
                    LOG.WriteLog(eEvent.ERR_RESERVOIR, _moduleName, $"Dose Flow {flow} mL/min is not over {_flowRateMin} mL/min");
                    return true;
                }
            }
            
            if (CheckPOSStatus(status, POSStatus.Error))
            {
                LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, $"POSStatus:{status}. Error is true");
                return true;
            }
            return false;
        }
        /// 
        /// DoneOperation
        /// 
        /// 
        private bool DoneOperation()
        {
            string ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_EXECUTE}");
            bool result = IOModuleManager.Instance.WriteIoValue(ioName, false);
            if (!result)
            {
                return false;
            }
            ioName = BeckhoffModuleIOManager.Instance.GetIoNameByInnerModuleName($"{Module}.{TRANSFER_PUMP_ENABLE}");
            result = IOModuleManager.Instance.WriteIoValue(ioName, false);
            if (!result)
            {
                return false;
            }
            if (!_cmReservoirDevice.CrossDoseOff("", null))
            {
                LOG.WriteLog(eEvent.WARN_RESERVOIR, _moduleName, $"Close CrossDose Valve failed");
                return false;
            }
            _persistentValue.AutoCrossDoseStartTime = DateTime.MinValue;
            _persistentValue.AutoCrossDoseStartAmpHour = 0;
            _cmReservoirDevice.ReservoirData.RemainingCrossDoseVolume = 0;
            ReservoirsPersistentManager.Instance.UpdatePersistentValue(_moduleName);
            return true;
        }
        /// 
        /// STM状态判断
        /// 
        /// 
        /// 
        /// 
        private bool CheckSTMStatus(ushort status, STMStatus statusName)
        {
            bool result = false;
            switch (statusName)
            {
                case STMStatus.ReadyToEnable:
                    byte bit0 = (byte)(status & 0b0001);
                    result = (bit0 == 1) ? true : false;
                    break;
                case STMStatus.Ready:
                    byte bit1 = (byte)((status & 0b0010) >> 1);
                    result = (bit1 == 1) ? true : false;
                    break;
                case STMStatus.Warning:
                    byte bit2 = (byte)((status & 0b0100) >> 2);
                    result = (bit2 == 1) ? true : false;
                    break;
                case STMStatus.Error:
                    byte bit3 = (byte)((status & 0b1000) >> 3);
                    result = (bit3 == 1) ? true : false;
                    break;
                default:
                    break;
            }
            return result;
        }
        /// 
        /// POS状态判断
        /// 
        /// 
        /// 
        /// 
        private bool CheckPOSStatus(ushort status, POSStatus statusName)
        {
            bool result = false;
            switch (statusName)
            {
                case POSStatus.Busy:
                    byte bit0 = (byte)(status & 0b0001);
                    result = (bit0 == 1) ? true : false;
                    break;
                case POSStatus.InTarget:
                    byte bit1 = (byte)((status & 0b0010) >> 1);
                    result = (bit1 == 1) ? true : false;
                    break;
                case POSStatus.Warning:
                    byte bit2 = (byte)((status & 0b0100) >> 2);
                    result = (bit2 == 1) ? true : false;
                    break;
                case POSStatus.Error:
                    byte bit3 = (byte)((status & 0b1000) >> 3);
                    result = (bit3 == 1) ? true : false;
                    break;
                default:
                    break;
            }
            return result;
        }
    }
}