using ProjectBase.Data.Redis;
using SIASUN.Autopilot.Device.EventMessage;
using SIASUN.Autopilot.Model;
using System;
using TwinCAT.Ads;

namespace SIASUN.Autopilot.Device.BeckHoff
{
    /// <summary>
    /// 类型限定为 string、boolen、int、double、结构体
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class TwinCatAds<T> : AbsTwinCatOpc
    {        
        #region 变量
        private int iHandle;
        private int notificationHandle;
        private TcAdsClient adsClient =null;
        private T strValue;
        private MoBeckhoffNode moBFNodeInfo = null;
        private object objUserData { get;  set; }
        #endregion
        private RedisHelper redis =null;
        public TwinCatAds(MoBeckhoffNode mNodeInfo):base()
        {
            try
            {
                moBFNodeInfo = mNodeInfo;
                objUserData = mNodeInfo.NodeName;
                this.adsClient = base.GetAds();
                ConnectOpc();
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }

        #region 方法
        public bool ConnectOpc()
        {           
            if (redis == null)
                redis = new RedisHelper();

            if (!adsClient.IsConnected)
            {
                adsClient.Connect(moBFNodeInfo.IP, moBFNodeInfo.Port);
            }
            adsClient.AdsNotificationEx += new AdsNotificationExEventHandler(adsClient_AdsNotificationEx);

            iHandle = adsClient.CreateVariableHandle(moBFNodeInfo.NodeName);
            if (moBFNodeInfo.ReceiveType != 1)
            {                
                notificationHandle = adsClient.AddDeviceNotificationEx(moBFNodeInfo.NodeName, AdsTransMode.OnChange, 10, 0, objUserData, typeof(T));
            }                   
            return true;
        }        

        public override bool DisConnectOpc()
        {
            try
            {
                DeleteNotification();
                adsClient.Dispose();
                iHandle = 0;
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            return true;
        }

        public override bool IsConnectOpc()
        {
            return adsClient.IsConnected;
        }

        public override bool CreateNotification()
        {
            try
            {
                notificationHandle = adsClient.AddDeviceNotificationEx(moBFNodeInfo.NodeName, AdsTransMode.OnChange, 10, 0, objUserData, typeof(T));
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            return true;
        }

        public override bool DeleteNotification()
        {
            try
            {
                if (notificationHandle != 0)
                {
                    adsClient.DeleteDeviceNotification(notificationHandle);
                    notificationHandle = 0;
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            return true;
        }

        private void adsClient_AdsNotificationEx(object sender, AdsNotificationExEventArgs e)
        {
            if (!e.NotificationHandle.Equals(notificationHandle))
                return;

            strValue = (T)e.Value;
            redis.SetHash<T>("plc_node", e.UserData.ToString(), strValue);
            EventMessages.GetSingleton().Publish(e.UserData.ToString(), typeof(T).ToString(), e.Value.ToString());
        }
     
        public override void Set(object nodevalue, String datatype)
        {
            switch (datatype)
            {
                case "double":
                    adsClient.WriteAny(iHandle, Convert.ToDouble(nodevalue));
                    break;
                case "int":
                    adsClient.WriteAny(iHandle, Convert.ToInt16(nodevalue));
                    break;
                case "uint":
                    adsClient.WriteAny(iHandle, Convert.ToUInt16(nodevalue));
                    break;
                case "dint":
                    adsClient.WriteAny(iHandle, Convert.ToInt32(nodevalue));
                    break;
                case "word":
                    adsClient.WriteAny(iHandle, Convert.ToInt16(nodevalue));
                    break;
                case "bool":
                    adsClient.WriteAny(iHandle, Convert.ToBoolean(nodevalue));
                    break;
                case "byte":
                    adsClient.WriteAny(iHandle, Convert.ToByte(nodevalue));
                    break;
                case "real":
                    adsClient.WriteAny(iHandle, Convert.ToSingle(nodevalue));
                    break;
                default:
                    throw new IndexOutOfRangeException($"TwinCatAds get {objUserData} error, value = { Convert.ToSingle(nodevalue)}, no match type.");
                    //break;
            };
        }

        public override object Get(out Type _type)
        {
            strValue = (T)adsClient.ReadAny(iHandle, typeof(T));
            _type = typeof(T);
            redis.SetHash<T>("plc_node", objUserData.ToString(), strValue);
            return strValue;
        }
        #endregion
    }
}
