using OPC_Client;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Xml.Linq;

namespace EBRDemo
{
    class SCADA
    {

        private OPC_Client.Client client = new OPC_Client.Client();

        /// <summary>
        /// 第一个string代表唯一设备编码，第二个string代表批次信息
        /// </summary>
        private Dictionary<string, string> BatchNums = new Dictionary<string, string>();
        //每10s在屏幕显示一次值
        private static System.Timers.Timer timer = new System.Timers.Timer(5000);
          private string _opcServerName = "Studio.Scada.OPC.5";//现场服务器名称
       // private string _opcServerName = "Kepware.KEPServerEX.V6";//测试服务器名称
        /// <summary>
        /// 添加采集点位
        /// </summary>
        public void AddItems()
        {
            string path = @"采集点位.csv";//采集点位item，需要与实际一致
           
            DataTable table = ReadCSV(path);

            if (table.Columns.Count == 3)
            {
                foreach (DataRow row in table.Rows)
                {
                    client.itemIDs.Add(row[0].ToString());
                    client.descritions.Add(row[1].ToString());
                    client.equipMentCodes.Add(row[2].ToString());
                }

                //建立连接
                client._opcServerName = _opcServerName;
                client.Connect();
                if (client.isConnected)
                {
                    //MessageBox.Show("未连接成功");
                    Console.WriteLine("连接成功");
                }
                else
                {
                    Console.WriteLine("未连接成功");
                }
                //数据变化时
                client.Data_Change += Client_DataChange;
                //异步写入多个值的回调方法
                client.Async_WriteMuchComplete += AsyncWriteMuchComplete;

                timer.Elapsed += OnTimedEvent;
                timer.AutoReset = true;
                timer.Enabled = true;
            }
        }
        private void OnTimedEvent(Object source, ElapsedEventArgs e)
        {
            Console.WriteLine("每10s输出一次值");
            //WriteSingleValue("数据类型示例.8 位设备.S 寄存器.String4", "demossss");
            //WriteMuchValue();
            foreach (OPC_Client.Data data in client.dataCenter.Values)
            {
                Console.WriteLine(data.itemID + ":" + data.value.ToString());
                Console.WriteLine("数据类型" + ":" + data.dataType.ToString());
            }

        }

        /// <summary>
        /// 写入值回调方法
        /// TransactionID，人为设置规定的标识ID
        /// </summary>
        /// <param name="TransactionID"></param>
        /// <param name="NumItems"></param>
        /// <param name="ClientHandles"></param>
        /// <param name="Errors"></param>
        private void AsyncWriteMuchComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors)
        {

        }
        /// <summary>
        /// NumItems 变化的数据数目
        /// ClientHandles clientID 
        /// </summary>
        /// <param name="NumItems"></param>
        /// <param name="ClientHandles"></param>        
        private void Client_DataChange(int NumItems, ref Array ClientHandles)
        {

        }
        #region 数据更改
        /// <summary>
        /// 刷新所有数据，不需要高频使用
        /// </summary>
        private void AsyncRefreshAll()
        {
            client.AsyncRefreshAll();
        }
        /// <summary>
        /// 更新批次号
        /// </summary>
        private void UpdateBatchNum()
        {
            foreach (Data data in client.dataCenter.Values)
            {
                string batchNum = "";
                //根据设备编码获取当前批次信息
                if (BatchNums.TryGetValue(data.equipMentCode, out batchNum))
                    data.batchNum = batchNum;

            }
        }
        /// <summary>
        /// 写单个值
        /// </summary>
        private void WriteSingleValue(string item,object obj_value)
        {
            try
            {
                //根据item值，更新值                
                //如果数据类型不对，可能导致错误
                client.WriteOneValue(item, obj_value);               
            }
            catch(Exception err)
            {
                Console.WriteLine(err.Message); 
            }
        }
        /// <summary>
        /// 写多个值
        /// </summary>
        private void WriteMuchValue()
        {
            try
            {
                List<string> itemIds = new List<string>();
                itemIds.Add("通道 1.设备 1.标记 1");
                itemIds.Add("通道 1.设备 1.标记 2");
                itemIds.Add("通道 1.设备 1.标记 3");
                itemIds.Add("数据类型示例.8 位设备.S 寄存器.String1");
                List<object> values = new List<object>();
                values.AddRange(new List<object> { 17, 18, 19, "20" });
                Array erros;
                int TransactionID = 8;
                int cancelID;
                //写入方法
                client.AsyncWriteMuchValue(itemIds.Count, itemIds, values, out erros, TransactionID, out cancelID);
            }
            catch
            {

            }
        }

        #endregion;
        #region 自定义方法，可以不看

        /// 给定文件的路径，读取文件的二进制数据，判断文件的编码类型
        /// <param name="FILE_NAME">文件路径</param>
        /// <returns>文件的编码类型</returns>
        public static System.Text.Encoding GetType(string FILE_NAME)
        {
            System.IO.FileStream fs = new System.IO.FileStream(FILE_NAME, System.IO.FileMode.Open,
                System.IO.FileAccess.Read);
            System.Text.Encoding r = GetType(fs);
            fs.Close();
            return r;
        }
        /// 通过给定的文件流，判断文件的编码类型
        /// <param name="fs">文件流</param>
        /// <returns>文件的编码类型</returns>
        public static System.Text.Encoding GetType(System.IO.FileStream fs)
        {
            byte[] Unicode = new byte[] { 0xFF, 0xFE, 0x41 };
            byte[] UnicodeBIG = new byte[] { 0xFE, 0xFF, 0x00 };
            byte[] UTF8 = new byte[] { 0xEF, 0xBB, 0xBF }; //带BOM
            System.Text.Encoding reVal = System.Text.Encoding.Default;

            System.IO.BinaryReader r = new System.IO.BinaryReader(fs, System.Text.Encoding.Default);
            int i;
            int.TryParse(fs.Length.ToString(), out i);
            byte[] ss = r.ReadBytes(i);
            if (IsUTF8Bytes(ss) || (ss[0] == 0xEF && ss[1] == 0xBB && ss[2] == 0xBF))
            {
                reVal = System.Text.Encoding.UTF8;
            }
            else if (ss[0] == 0xFE && ss[1] == 0xFF && ss[2] == 0x00)
            {
                reVal = System.Text.Encoding.BigEndianUnicode;
            }
            else if (ss[0] == 0xFF && ss[1] == 0xFE && ss[2] == 0x41)
            {
                reVal = System.Text.Encoding.Unicode;
            }
            r.Close();
            return reVal;
        }
        /// 判断是否是不带 BOM 的 UTF8 格式
        /// <param name="data"></param>
        /// <returns></returns>
        private static bool IsUTF8Bytes(byte[] data)
        {
            int charByteCounter = 1;  //计算当前正分析的字符应还有的字节数
            byte curByte; //当前分析的字节.
            for (int i = 0; i < data.Length; i++)
            {
                curByte = data[i];
                if (charByteCounter == 1)
                {
                    if (curByte >= 0x80)
                    {
                        //判断当前
                        while (((curByte <<= 1) & 0x80) != 0)
                        {
                            charByteCounter++;
                        }
                        //标记位首位若为非0 则至少以2个1开始 如:110XXXXX...........1111110X　
                        if (charByteCounter == 1 || charByteCounter > 6)
                        {
                            return false;
                        }
                    }
                }
                else
                {
                    //若是UTF-8 此时第一位必须为1
                    if ((curByte & 0xC0) != 0x80)
                    {
                        return false;
                    }
                    charByteCounter--;
                }
            }
            if (charByteCounter > 1)
            {
                throw new Exception("非预期的byte格式");
            }
            return true;
        }
        //导入csv文件，区分英文引号和英文逗号
        //返回字符串中特定字符个数
        private int GetNum_String_Special(string str1, string str2)
        {
            int i = 0;
            if (str1.Contains(str2))
            {
                string str3;
                str3 = str1.Replace(str2, "");
                i = (str1.Length - str3.Length) / str2.Length;
            }
            else
            {
                i = 0;
            }
            return i;
        }
        //判断一个数是奇数还是偶数,false代表偶数，true代表奇数
        //fdf 
        private bool EorO(int i)
        {
            bool b = false;
            if (i % 2 == 1)
            {
                b = true;
            }
            else
            {
                b = false;
            }
            return b;
        }
        //修正csv文件中含有特殊字符（英文逗号，英文引号）
        //读取是按行进行的
        private List<string> Amend_str(string csv)
        {
            string newStr = "";
            string[] str_old = csv.Split(',');
            List<string> str_new = new List<string>();
            bool isSplice = false;//是否需要连接后面字符串
            foreach (string str in str_old)
            {
                int i = 0;
                i = GetNum_String_Special(str, "\"");
                //为奇数代表一定含有英文逗号
                //直到获取第一个奇数
                if (!isSplice)
                {
                    //从0到1的跳变，代表英文逗号分隔符的起始第一个
                    if (EorO(i))
                    {
                        isSplice = true;//表示需要与后面的字符串进行连接
                        newStr += str + @",";
                    }
                    //为偶数，则代表不含英文逗号，可能含有英文引号
                    else
                    {
                        //一个引号代替两个引号
                        newStr = str.Replace("\"\"", "\"");
                        str_new.Add(newStr);
                        newStr = string.Empty;
                    }
                }
                //表示后面的均为需要进行连接的字符串，如果后面的字符串中英文引号为单，则表示连接到此为止
                else
                {
                    //奇数，则代表连接到此为止
                    if (EorO(i))
                    {
                        newStr += str;
                        newStr = newStr.Replace("\"\"", "\"");
                        str_new.Add(newStr);
                        newStr = string.Empty;
                        isSplice = false;
                    }
                    //为偶数，则表示不是最后一个连接项
                    else
                    {
                        newStr += str + @",";
                    }
                }
            }
            return str_new;
        }
        //读取文件,返回datatable,head代表是否读取首行标题，head不可取，必须要
        //读取标题行，否则返回table容易出错，当CSV格式首航单元格不是最多的时候，易丢失数据
        private DataTable ReadCSV(string filePath)
        {
            DataTable dt = new DataTable();
            System.Text.Encoding encoding = GetType(filePath);
            System.IO.StreamReader sr = new System.IO.StreamReader(filePath, encoding); //记录每次读取的一行记录
            string strLine = "";
            //记录每行记录中的各字段内容
            string[] aryLine = null;
            string[] tableHead = null;
            //标示列数
            int columnCount = 0;
            //标示是否是读取的第一行
            bool IsFirst = true;
            //逐行读取CSV中的数据
            while ((strLine = sr.ReadLine()) != null)
            {
                if (IsFirst)
                {
                    tableHead = Amend_str(strLine).ToArray();
                    IsFirst = false;
                    columnCount = tableHead.Length;
                    //创建列
                    for (int i = 0; i < columnCount; i++)
                    {
                        DataColumn dc = new DataColumn(tableHead[i]);
                        dt.Columns.Add(dc);
                    }
                }
                else
                {
                    aryLine = Amend_str(strLine).ToArray();
                    DataRow dr = dt.NewRow();
                    for (int j = 0; j < columnCount; j++)
                    {
                        dr[j] = aryLine[j];
                    }
                    dt.Rows.Add(dr);
                }
            }
            sr.Close();
            return dt;
        }
       
      //  private object obj_GetDataTable = new object();
       
        /// <summary>
        /// 连接数据库，获取table
        /// </summary>
        /// <param name="command"></param>
        /// <param name="conn"></param>
        /// <returns></returns>
        private DataTable GetDataTable(string command, string conn)
        {
            DataTable table = new DataTable();
            //连接SQL数据库
            System.Data.SqlClient.SqlConnection sqlConnection;
            System.Data.SqlClient.SqlCommand sqlCommand;
            try
            {
                //查询数据库SQL                                
                sqlConnection = new System.Data.SqlClient.SqlConnection(conn);
                sqlConnection.Open();
                sqlCommand = new System.Data.SqlClient.SqlCommand(command, sqlConnection);
                System.Data.SqlClient.SqlDataAdapter sda = new System.Data.SqlClient.SqlDataAdapter();
                sda.SelectCommand = sqlCommand;
                DataSet ds = new DataSet();
                sda.Fill(ds, "cs");
                table = ds.Tables[0];
                sqlConnection.Close();
            }
            catch (Exception err)
            {
                // MessageBox.Show(err.Message);
                Console.WriteLine(DateTime.Now.ToString() + ":" + err.Message);
               
            }
            return table;
        }
        #endregion
    }
}
