using System;
using OPC;
using OPCDA;
using OPCDA.NET;
using System.Runtime.InteropServices;

namespace Mesnac.Basic
{
    // 定义用于返回发生变化的项的值和其对应的客户句柄
    public delegate void DataChange(READRESULT[] values, string[] itemsID);

    /// <summary>
    /// 自定义的结构
    /// 用来保存读取到的数据(值,品质,时间戳)
    /// </summary>
    public struct READRESULT
    {
        public string readValue;
        public string readQuality;
        public string readTimeStamp;
    }

    public class OPCComm
    {
        OpcServer myOPCServer;//server object
        SyncIOGroup readWriteGroup;//group object
        ItemDef itemData;
        BrowseTree itemTree;
        READRESULT readRes;
        //供回调函数使用
        RefreshGroup asyncRefrGroup;
        DataChangeEventHandler dch;
        READRESULT[] dataChangeRes; //用来保存变量值发生变化时服务器的回调函数得到的变量数据

        public READRESULT[] DataChangeRes
        {
            get { return dataChangeRes; }
            set { dataChangeRes = value; }
        }   
       
        public BrowseTree ItemTree
        {
            get { return itemTree; }
            set { itemTree = value; }
        }

        public ItemDef ItemData
        {
            get { return itemData; }
            set { itemData = value; }
        }

        internal READRESULT ReadRes
        {
            get { return readRes; }
            set { readRes = value; }
        }

        DataChange dtChange;
        public OPCComm(DataChange dataChange)
        {
            dtChange = dataChange;
        }

        #region 获得可访问的服务器名字列表
        /// <summary>
        ///  获得服务器名字列表
        /// </summary>
        /// <returns>返回包含服务器名字的字符串数组</returns>
        public string[] GetSerList(string host)
        {
            OpcServerBrowser serList = new OpcServerBrowser(new OPC.Common.Host(host));
            string[] serNames;
            serList.GetServerList(out serNames);

            return serNames;
        }
        #endregion

        #region 浏览服务器地址空间
        /// <summary>
        /// 浏览服务器地址空间
        /// </summary>
        /// <returns>成功返回0, 失败返回-1</returns>
        public int GetItemList()
        {
            itemTree = new BrowseTree(myOPCServer);

            int res = itemTree.CreateTree();		// Browse server from root
            if (HRESULTS.Succeeded(res))
            {
                return 0;
            }

            return -1;           
        }
        #endregion

        #region 连接服务器
        /// <summary>
        /// 连接服务器
        /// </summary>
        /// <param name="serName">服务器的名字</param>
        /// <param name="serIP">服务器所在机器的IP</param>
        /// <returns>成功返回0,失败返回-1</returns>     
        public int Conn2Server(string serName, string serIP,int refresh)
        {
            int res;
            try
            {
                myOPCServer = new OpcServer();

                res = myOPCServer.Connect(serIP, serName);
                if (HRESULTS.Failed(res))
                {
                    myOPCServer = null;
                    return -1;
                }

                readWriteGroup = new SyncIOGroup(myOPCServer);
                //供回调函数刷新变量值使用
                dch = new DataChangeEventHandler(DataChangeHandler);
                if (refresh<=0)
                {
                    refresh = 1000;
                }
                asyncRefrGroup = new RefreshGroup(myOPCServer, dch, refresh);
            }
            catch
            {
                myOPCServer = null;
                return -1;
            }
            return 0;
        }
        #endregion 
       
        #region 添加项
        /// <summary>
        /// 获得变量对象
        /// </summary>
        /// <param name="itemId">变量ID</param>
        /// <returns>成功返回0, 失败返回-1</returns>
        int AddItem(string itemId)
        {
            itemData = readWriteGroup.Item(itemId);
            if (itemData == null)
            {
                readWriteGroup.Add(itemId);
                itemData = readWriteGroup.Item(itemId);
                if (itemData == null)
                {
                    return -1;
                }
            }

            return 0;
        }
        #endregion

        #region 读取一个变量
        /// <summary>
        /// 从服务器上读取数据,读取的数据保存在READRESULT结构中
        /// </summary>
        /// <param name="num">变量ID</param>
        /// <returns>成功返回0, 失败返回-1</returns>
        public int Read(string itemId)
        {
            if (AddItem(itemId) == 0)
            {
                OPCItemState Rslt;
                OPCDATASOURCE dsrc = OPCDATASOURCE.OPC_DS_DEVICE;
                int rtc = readWriteGroup.Read(dsrc, itemData, out Rslt);

                if (HRESULTS.Succeeded(rtc))		// read from OPC server successful
                {
                    if (Rslt != null)
                    {
                        if (HRESULTS.Succeeded(Rslt.Error))	// item read successful
                        {
                            readRes.readValue = Rslt.DataValue.ToString();
                            readRes.readQuality = readWriteGroup.GetQualityString(Rslt.Quality);
                            DateTime dt = DateTime.FromFileTime(Rslt.TimeStamp);
                            readRes.readTimeStamp = dt.ToString();

                            return 0;
                        }
                        return -1;
                    }
                    return -1;
                }
                else
                {
                    return -1;
                }
            }
            return -1;
        }
        #endregion

        #region 修改一个变量
        /// <summary>
        /// 修改一个变量
        /// </summary>
        /// <param name="itemId">变量ID</param>
        /// <param name="value">变量的值</param>
        /// <returns>返回写操作结果的字符串</returns>
        public string Write(string itemId, object value)
        {
            if (AddItem(itemId) == 0)
            {
                int res = readWriteGroup.Write(itemData, value);

                return readWriteGroup.GetErrorString(res);
            }
            return null;
        }
        #endregion

        #region 向RefreshGroup组中添加一个变量
        /// <summary>
        /// 向RefreshGroup组中添加一个变量
        /// </summary>
        /// <param name="itemId">变量ID</param>
        /// <returns>成功返回0,失败返回-1</returns>
        public int Add2RefrGroup2(string itemId,ref VarEnum DataType)
        {
            DataType = VarEnum.VT_EMPTY;
            if (AddItem(itemId) == 0)
            {
                DataType = itemData.OpcIInfo.CanonicalDataType;
                int res = asyncRefrGroup.Add(itemData.OpcIDef.ItemID);
                if (HRESULTS.Failed(res))
                {
                    return -1;
                }
                return 0;
            }
            return -1;         
        }
        public int Add2RefrGroup(string itemId)
        {
           
            if (AddItem(itemId) == 0)
            {
                
                int res = asyncRefrGroup.Add(itemData.OpcIDef.ItemID);
                if (HRESULTS.Failed(res))
                {
                    return -1;
                }
                return 0;
            }
            return -1;
        }
        #endregion

        #region 断开同服务器的连接
        /// <summary>
        /// 断开同服务器的连接
        /// </summary>
        /// <returns>成功返回0,失败返回-1</returns>
        public void  DisConn()
        {
            if (myOPCServer != null)
            {
                asyncRefrGroup.Dispose();
                readWriteGroup.Dispose();
                myOPCServer.Disconnect();
                myOPCServer = null;
            }
        }       
        #endregion

        #region 回调函数
        /// <summary>
        /// 回调函数,当RefreshGroup组里的数据发生变化时由服务器自动调用该函数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void DataChangeHandler(object sender, DataChangeEventArgs e)
        {
            int count = e.sts.Length;
            string[] itemsID = new string[count];
            dataChangeRes = new READRESULT[count];

            for (int i = 0; i < count; ++i)
            {
                int hnd = e.sts[i].HandleClient;
                ItemDef item = asyncRefrGroup.FindClientHandle(hnd);
                if (item != null)
                {
                    //保存变量的ItemID
                    itemsID[i] = item.OpcIDef.ItemID;
                }

                object val = e.sts[i].DataValue;
                string qt = asyncRefrGroup.GetQualityString(e.sts[i].Quality);
                DateTime dt = DateTime.FromFileTime(e.sts[i].TimeStamp);
                //将变量的值保存在READRESULT结构中
                dataChangeRes[i].readValue = val.ToString();
                dataChangeRes[i].readQuality = qt;
                dataChangeRes[i].readTimeStamp = dt.ToString();
            }

            dtChange(dataChangeRes, itemsID);
        }
        #endregion
    }
}