using Aitex.Core.RT.Event;
using Aitex.Core.RT.IOCore;
using Aitex.Core.RT.Log;
using Aitex.Core.RT.SCCore;
using Aitex.Core.Util;
using System;
using System.Threading.Tasks;
using System.Xml;

namespace Aitex.Core.RT.Device.Devices
{
    internal class IoHeartBeat : BaseDevice, IDevice
    {
        public float Feedback
        {
            get
            {
                if (_ai != null)
                {
                    return _isFloatAioType ? _ai.Value : _ai.Value;
                }

                return 0;
            }
        }

        public float SetPoint
        {
            get
            {
                if (_ao != null)
                {
                    return _isFloatAioType ? _ao.Value : _ao.Value;
                }

                return 0;
            }
            set
            {
                if (_ao != null)
                {
                    if (_isFloatAioType)
                        _ao.Value = value;
                    else
                        _ao.Value = (short)value;
                }
            }
        }

        //IO
        private AIAccessor _ai = null;

        private AOAccessor _ao = null;

        private bool _isFloatAioType = false;

        private readonly DeviceTimer _timHeartBeatOfRange = new();

        private readonly R_TRIG _trigHeartBeatError = new();

        private short _counter = 0;

        private PeriodicJob _thread;

        private bool _isModuleInstalled;

        public IoHeartBeat(string module, XmlElement node, string ioModule = "")
        {
            base.Module = module;
            base.Name = node.GetAttribute("id");
            base.Display = node.GetAttribute("display");
            base.DeviceID = node.GetAttribute("schematicId");

            _ai = ParseAiNode("ai", node, ioModule);
            _ao = ParseAoNode("ao", node, ioModule);

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

        public bool Initialize()
        {
            _thread = new PeriodicJob(1000, OnTimer, "PLC Write Thread", false);

            _isModuleInstalled= SC.SafeGetValue($"System.SetUp.Is{Module}Installed", false);

            if (!SC.SafeGetValue($"System.IsSimulatorMode", false))//获取不到值时，也要开启心跳
                Task.Delay(1000 * 30).ContinueWith((a) => _thread.Start()); //防止UI没启动，就进行EV.Post



            return true;
        }

        public bool OnTimer()
        {
            try
            {
                if (!_isModuleInstalled)//模块未安装直接退出
                    return true;

                if (Feedback != SetPoint && _timHeartBeatOfRange.IsIdle())//检测值是否相等
                    _timHeartBeatOfRange.Start(1000 * 10);

                if (Feedback == SetPoint)//有数据搬运机制，把AO中的数值，映射到AI中，所以二者会实时相同，
                {
                    SetHeartBeatValue();//执行每秒自加一
                    _timHeartBeatOfRange.Stop();
                }
                    
                _trigHeartBeatError.CLK = _timHeartBeatOfRange.IsTimeout();//超时后才会置true

                if (_trigHeartBeatError.Q)
                    EV.PostAlarmLog(Module, $"Alarm:PLC heartbeat error , Time ovre 10s");
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }

            return true;
        }

        private void SetHeartBeatValue()
        {
            _counter++;

            if (_counter > 1000)
                _counter = 0;

            SetPoint = _counter;
        }

        public void Terminate()
        {
        }

        public void Monitor()
        {
        }

        public void Reset()
        {
            _trigHeartBeatError.RST = true;
        }
    }
}