using Aitex.Core.Common.DeviceData.IoDevice;
using Aitex.Core.RT.DataCenter;
using Aitex.Core.RT.Event;
using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.OperationCenter;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using System;
using System.Xml;


namespace Aitex.Core.RT.Device.Devices
{
    public class IoPSU : BaseDevice, IDevice
    {
        #region Variables

        private float _resLimitMax;
        private float _outputLimitHighTuningTemp = 1600;

        private readonly bool _isFloatAioType;
        private readonly AIAccessor _aiOutputVoltage;
        private readonly AIAccessor _aiOutputArms;
        private readonly AIAccessor _aiOutputPower;
        private readonly AIAccessor _aiSimVoltage;
        private readonly AIAccessor _aiSimArms;

        //private AOAccessor _aoEnable = null;
        //private AOAccessor _aoReset = null;
        private readonly AOAccessor _aoConstant;
        private readonly AOAccessor _aoOutputLimitHigh;
        private readonly AOAccessor _aoOutputLimitLow;


        private readonly DIAccessor _diStatus;
        private readonly DIAccessor _diAlarm;
        private readonly DIAccessor _diHeatEnable;
        private DIAccessor _diCommunicationError;


        private  DIAccessor _diPSUCommunicationError;//PSU总的报警
        private DIAccessor _diSCRCommunicationError;//SCR总的报警    二者任意报警，关闭所有加热
        private readonly R_TRIG _psuCommunicationError = new();

        private readonly DOAccessor _doReset;
        private readonly DOAccessor _doStatus;
        private readonly DOAccessor _doHeatEnable;
        private readonly DOAccessor _doRelatedEnable; //每个Enable同时关联的Inner，Middle，Out Enable

        private readonly R_TRIG _alarmTrig = new();
        private readonly R_TRIG _commAlarmTrig = new();
        private readonly R_TRIG _enableTrig = new();
        private readonly R_TRIG _enableTrig2 = new();

        

        private readonly R_TRIG _trigVoltage = new();
        private readonly R_TRIG _trigCurrent = new();

        private readonly R_TRIG _trigResOutOfRange = new();

        private readonly DeviceTimer _timResetPulse = new();
        private readonly DeviceTimer _timResOutOfRange = new();

        private readonly IoPsuData _deviceData = new();
        #endregion

        #region Constructors

        public IoPSU(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule)
        {
            _aiOutputVoltage = ParseAiNode("aiOutputVoltage", node, ioModule);
            _aiOutputArms = ParseAiNode("aiOutputArms", node, ioModule);
            _aiOutputPower = ParseAiNode("aiOutputPower", node, ioModule);
            _aiSimVoltage = ParseAiNode("aiSimVoltage", node, ioModule);
            _aiSimArms = ParseAiNode("aiSimArms", node, ioModule);

            _aoConstant = ParseAoNode("aoConstant", node, ioModule);
            _aoOutputLimitHigh = ParseAoNode("aoOutputLimitHigh", node, ioModule);
            _aoOutputLimitLow = ParseAoNode("aoOutputLimitLow", node, ioModule);

            _doReset = ParseDoNode("doReset", node, ioModule);
            _doStatus = ParseDoNode("doStatus", node, ioModule);
            _diStatus = ParseDiNode("diStatus", node, ioModule);
            _diAlarm = ParseDiNode("diAlarm", node, ioModule);
            _doHeatEnable = ParseDoNode("doHeatEnable", node, ioModule);
            _diHeatEnable = ParseDiNode("diHeatEnable", node, ioModule);
            _doRelatedEnable = ParseDoNode("doRelatedEnable", node, ioModule);
            _diCommunicationError = ParseDiNode("diCommunicationError", node, ioModule);

            _diPSUCommunicationError = ParseDiNode("diPSUCommunicationError", node, ioModule);
            _diSCRCommunicationError = ParseDiNode("diSCRCommunicationError", node, ioModule);

            _isFloatAioType = !string.IsNullOrEmpty(node.GetAttribute("aioType")) && (node.GetAttribute("aioType") == "float");
        }

        #endregion

        #region Properties

        public float OutputVoltageFeedBack => _aiOutputVoltage == null ? 0 : (_isFloatAioType ? _aiOutputVoltage.Value : _aiOutputVoltage.Value);

        public float OutputArmsFeedBack => _aiOutputArms == null ? 0 : (_isFloatAioType ? _aiOutputArms.Value : _aiOutputArms.Value);

        public float ResistanceLimitMax => _resLimitMax;

        public float Resistance => OutputArmsFeedBack == 0 ? 0 : OutputVoltageFeedBack / OutputArmsFeedBack;

        public bool IsResistanceOutOfRange => IsResistanceTooHigh;

        public bool IsResistanceTooHigh
        {
            get;
            private set;
        }

        public float OutputPowerFeedBack => _aiOutputPower == null ? 0 : (_isFloatAioType ? _aiOutputPower.Value : _aiOutputPower.Value);

        public bool StatusFeedBack => _diStatus?.Value ?? false;

        public float SimVoltageFeedBack => _aiSimVoltage == null ? 0 : (_isFloatAioType ? _aiSimVoltage.Value : _aiSimVoltage.Value);

        public float SimArmsFeedBack => _aiSimArms == null ? 0 : (_isFloatAioType ? _aiSimArms.Value : _aiSimArms.Value);

        public bool AlarmFeedBack => _diAlarm?.Value ?? false;

        public float ConstantSetPoint
        {
            get => _aoConstant == null ? 0 : (_isFloatAioType ? _aoConstant.Value : _aoConstant.Value);
            set
            {
                if (_isFloatAioType)
                {
                    _aoConstant.Value = value;
                }
                else
                {
                    _aoConstant.Value = (short)value;
                }
            }
        }

        public float OutputLimitHigh => _aoOutputLimitHigh?.Value ?? 0;

        public float OutputLimitLow => _aoOutputLimitLow?.Value ?? 0;

        public bool AllHeatEnable => _diHeatEnable?.Value ?? false;

        private IoPsuData DeviceData
        {
            get
            {
                _deviceData.IsAlarm = AlarmFeedBack;
                _deviceData.OutputStatusFeedBack = StatusFeedBack;
                _deviceData.AllHeatEnable = AllHeatEnable;
                _deviceData.ConstantSetPoint = ConstantSetPoint;
                _deviceData.OutputArmsFeedBack = OutputArmsFeedBack;
                _deviceData.OutputPowerFeedBack = OutputPowerFeedBack;
                _deviceData.OutputVoltageFeedback = OutputVoltageFeedBack;
                _deviceData.ResistanceLimitMax = ResistanceLimitMax;
                _deviceData.Resistance = Resistance;
                _deviceData.IsResistanceOutOfRange = IsResistanceOutOfRange;
                _deviceData.IsResistanceTooHigh = IsResistanceTooHigh;
                _deviceData.OutputLimitHighTuningTemp = _outputLimitHighTuningTemp;
                _deviceData.OutputLimitHigh = OutputLimitHigh;
                _deviceData.OutputLimitLow = OutputLimitLow;
                return _deviceData;
            }
        }

        public Func<bool, bool> FuncCheckInterLock { get; set; }

        #endregion

        #region Methods

        public bool Initialize()
        {
            DATA.Subscribe($"{Module}.{Name}.OutputVoltageFeedBack", () => OutputVoltageFeedBack);
            DATA.Subscribe($"{Module}.{Name}.OutputArmsFeedBack", () => OutputArmsFeedBack);
            DATA.Subscribe($"{Module}.{Name}.ResistanceLimitMax", () => ResistanceLimitMax);
            DATA.Subscribe($"{Module}.{Name}.Resistance", () => Resistance);
            DATA.Subscribe($"{Module}.{Name}.IsResistanceOutOfRange", () => IsResistanceOutOfRange);
            DATA.Subscribe($"{Module}.{Name}.IsResistanceTooHigh", () => IsResistanceTooHigh);
            DATA.Subscribe($"{Module}.{Name}.OutputPowerFeedBack", () => OutputPowerFeedBack);
            DATA.Subscribe($"{Module}.{Name}.StatusFeedBack", () => StatusFeedBack);
            DATA.Subscribe($"{Module}.{Name}.SimVoltageFeedBack", () => SimVoltageFeedBack);
            DATA.Subscribe($"{Module}.{Name}.SimArmsFeedBack", () => SimArmsFeedBack);
            DATA.Subscribe($"{Module}.{Name}.ConstantSetPoint", () => ConstantSetPoint);
            /*DATA.Subscribe($"{Module}.{Name}.OutputLimitHigh", () => OutputLimitHigh);
            DATA.Subscribe($"{Module}.{Name}.OutputLimitLow", () => OutputLimitLow);
            DATA.Subscribe($"{Module}.{Name}.OutputLimitHighTuningTemp", () => _outputLimitHighTuningTemp);*/

            DATA.Subscribe($"{Module}.{Name}.AlarmFeedBack", () => AlarmFeedBack);
            DATA.Subscribe($"{Module}.{Name}.AllHeatEnable", () => AllHeatEnable);
            DATA.Subscribe($"{Module}.{Name}.DeviceData", () => DeviceData);

            OP.Subscribe($"{Module}.{Name}.SetHeadHeaterEnable", (function, args) =>
            {
                var isTrue = Convert.ToBoolean(args[0]);
                SetHeadHeaterEnable(isTrue, out _);
                return true;
            });


            OP.Subscribe($"{Module}.{Name}.SetPSUEnable", (function, args) =>
            {
                var isTrue = Convert.ToBoolean(args[0]);
                SetPSUEnable(isTrue, out _);
                return true;
            });
            OP.Subscribe($"{Module}.{Name}.SetPSUReset", (function, args) =>
            {
                var isTrue = Convert.ToBoolean(args[0]);
                SetPSUReset(isTrue, out _);
                return true;
            });

            return true;
        }

        public bool SetPSUEnable(bool setValue, out string reason)
        {
            if (!_doStatus.Check(setValue, out reason))
            {
                EV.PostWarningLog(Module, reason);
                return false;
            }
            if (!_doRelatedEnable.Check(setValue, out reason))
            {
                EV.PostWarningLog(Module, reason);
                return false;
            }
            if (!_doStatus.SetValue(setValue, out reason))
            {
                EV.PostWarningLog(Module, reason);
                return false;
            }
            if (!_doRelatedEnable.SetValue(setValue, out reason))
            {
                EV.PostWarningLog(Module, reason);
                return false;
            }
            return true;
        }

        public bool SetHeadHeaterEnable(bool setValue, out string reason)
        {
            reason = "";
            if (FuncCheckInterLock != null)
            {
                if (!FuncCheckInterLock(setValue))
                {
                    EV.PostInfoLog(Module, $"Set PSU Enable fialed for Interlock!");
                    return false;
                }
            }

            if (!_doHeatEnable.Check(setValue, out reason))
            {
                EV.PostWarningLog(Module, reason);
                return false;
            }

            if (!_doHeatEnable.SetValue(setValue, out reason))
            {
                EV.PostWarningLog(Module, reason);
                return false;
            }

            return true;
        }


        public void SetOutputLimitHigh(double value)
        {
            SC.SetItemValue($"PM.{Module}.Heater.PSU2OutputLimitHigh", value);
        }

        public void SetOutputLimitLow(double value)
        {
            SC.SetItemValue($"PM.{Module}.Heater.PSU2OutputLimitLow", value);
        }


        public bool SetPSUReset(bool setValue, out string reason)
        {
            if (!_doReset.Check(setValue, out reason))
            {
                EV.PostWarningLog(Module, reason);
                return false;
            }
            if (!_doReset.SetValue(setValue, out reason))
            {
                EV.PostWarningLog(Module, reason);
                return false;
            }

            _timResetPulse.Start(1000);
            return true;
        }

        public bool CheckPSUEnable()
        {
            return _doStatus.Value;
        }

        public void Terminate()
        {
        }

        protected override void HandleMonitor()
        {
            try
            {
                MonitorEnableTimer();
                MonitorAlarm();
                MonitorDoResetDone();

                MonitorCheckPSUCommunicationError();



            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }
        }


        public void Reset()
        {
            _alarmTrig.RST = true;
            _commAlarmTrig.RST = true;

            _trigVoltage.RST = true;
            _trigCurrent.RST = true;

            _psuCommunicationError.RST = true;

            ResetResistanceMonitorResult();
        }

        private void MonitorDoResetDone()
        {
            if (_timResetPulse.IsTimeout())
            {
                _timResetPulse.Stop();
                if (_doReset.Value)
                    _doReset.Value = false;

                ResetResistanceMonitorResult();
            }
        }


        private void MonitorCheckPSUCommunicationError()
        {
            if ((_diPSUCommunicationError?.Value ?? false) || (_diSCRCommunicationError?.Value ?? false))
            {
                _psuCommunicationError.CLK = true;
                if (_psuCommunicationError.Q)
                {
                    EV.PostWarningLog(Module, $"PSU or SCR is communication error! Set {Name} heat enable");
                    SetPSUEnable(false, out _);
                }
            }
            else
            {
                _psuCommunicationError.RST = true;
            }
        }

        /// <summary>
        /// 复位加热器电阻监测结果。
        /// </summary>
        private void ResetResistanceMonitorResult()
        {
            IsResistanceTooHigh = false;
            _trigResOutOfRange.RST = true;
            _timResOutOfRange.Stop();
        }

        private void MonitorAlarm()
        {
            //检查电阻值是否在合理范围
            _resLimitMax = (float)SC.GetValue<double>($"PM.{Module}.Heater.{Name}ResistanceMax");
            var timeOut = SC.GetValue<int>($"PM.{Module}.Heater.ResistanceMonitorHysteresis");

            if (Resistance > _resLimitMax && _timResOutOfRange.IsIdle())
            {
                _timResOutOfRange.Start(timeOut * 1000);
            }

            if (Resistance <= _resLimitMax)
            {
                IsResistanceTooHigh = false;
                _timResOutOfRange.Stop();
            }

            _trigResOutOfRange.CLK = _timResOutOfRange.IsTimeout();
            if (_trigResOutOfRange.Q)
            {
                IsResistanceTooHigh = true;
                EV.PostWarningLog(Module, $"{Name} Current resistance {Resistance}ohm exceeds the high limit {_resLimitMax}ohm.");
            }
        }

        private void MonitorEnableTimer()
        {
            if (Name == "PSU2")
            {
                _enableTrig.CLK = !_diHeatEnable.Value;
                if (_enableTrig.Q)
                {
                    //断腔体加热
                    SC.SetItemValue($"PM.{Module}.OpenLidCountDownTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                }

                _enableTrig2.CLK = _diHeatEnable.Value;
                if (_enableTrig2.Q)
                {
                    SC.SetItemValue($"PM.{Module}.OpenLidCountDownTime", "");
                }
            }
        }

        #region PSU2 Output Limit (Obsolete)

        /*private readonly R_TRIG _trigBelow1600 = new();
        private readonly R_TRIG _trigAbove1600 = new();

        private void MonitorPsu2OutputLimit()
        {
            if (Name == "PSU2")
            {
                var tunedByRecipe = SC.GetValue<bool>($"PM.{Module}.Heater.PSU2OutputLimitHighTuningByRecipe");
                tunedByRecipe = false; // 该功能用于测试，在发行版上屏蔽
                if (!tunedByRecipe)
                {
                    var limitHigh = (float)SC.GetValue<double>($"PM.{Module}.Heater.PSU2OutputLimitHigh");
                    var limitLow = (float)SC.GetValue<double>($"PM.{Module}.Heater.PSU2OutputLimitLow");

                    _outputLimitHighTuningTemp =
                        (float)SC.GetValue<double>($"PM.{Module}.Heater.PSU2OutputLimitHighTuningTemp");


                    _trigAbove1600.CLK = AETemp >= _outputLimitHighTuningTemp;
                    if (_trigAbove1600.Q)
                        EV.PostInfoLog(Module, $"Temp above {_outputLimitHighTuningTemp}℃, limit PSU2 Output level to {limitLow}%-{limitHigh}%");

                    _trigBelow1600.CLK = AETemp < _outputLimitHighTuningTemp;
                    if (_trigAbove1600.Q)
                        EV.PostInfoLog(Module, $"Temp below {_outputLimitHighTuningTemp}℃, limit PSU2 Output level to 100%");


                    if (AETemp >= _outputLimitHighTuningTemp)
                    {
                        if (Math.Abs(_aoOutputLimitHigh.Value - limitHigh) > 1E-6)
                            _aoOutputLimitHigh.Value = limitHigh;

                        if (Math.Abs(_aoOutputLimitLow.Value - limitLow) > 1E-6)
                            _aoOutputLimitLow.Value = limitLow;
                    }
                    else
                    {
                        _aoOutputLimitHigh.Value = 100;
                        _aoOutputLimitLow.Value = 100;
                    }
                }
            }
        }*/

        #endregion


        #endregion
    }
}
