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(); /// /// 第一个string代表唯一设备编码,第二个string代表批次信息 /// private Dictionary BatchNums = new Dictionary(); //每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";//测试服务器名称 /// /// 添加采集点位 /// 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()); } } /// /// 写入值回调方法 /// TransactionID,人为设置规定的标识ID /// /// /// /// /// private void AsyncWriteMuchComplete(int TransactionID, int NumItems, ref Array ClientHandles, ref Array Errors) { } /// /// NumItems 变化的数据数目 /// ClientHandles clientID /// /// /// private void Client_DataChange(int NumItems, ref Array ClientHandles) { } #region 数据更改 /// /// 刷新所有数据,不需要高频使用 /// private void AsyncRefreshAll() { client.AsyncRefreshAll(); } /// /// 更新批次号 /// private void UpdateBatchNum() { foreach (Data data in client.dataCenter.Values) { string batchNum = ""; //根据设备编码获取当前批次信息 if (BatchNums.TryGetValue(data.equipMentCode, out batchNum)) data.batchNum = batchNum; } } /// /// 写单个值 /// private void WriteSingleValue(string item,object obj_value) { try { //根据item值,更新值 //如果数据类型不对,可能导致错误 client.WriteOneValue(item, obj_value); } catch(Exception err) { Console.WriteLine(err.Message); } } /// /// 写多个值 /// private void WriteMuchValue() { try { List itemIds = new List(); itemIds.Add("通道 1.设备 1.标记 1"); itemIds.Add("通道 1.设备 1.标记 2"); itemIds.Add("通道 1.设备 1.标记 3"); itemIds.Add("数据类型示例.8 位设备.S 寄存器.String1"); List values = new List(); values.AddRange(new List { 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 自定义方法,可以不看 /// 给定文件的路径,读取文件的二进制数据,判断文件的编码类型 /// 文件路径 /// 文件的编码类型 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; } /// 通过给定的文件流,判断文件的编码类型 /// 文件流 /// 文件的编码类型 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 格式 /// /// 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 Amend_str(string csv) { string newStr = ""; string[] str_old = csv.Split(','); List str_new = new List(); 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(); /// /// 连接数据库,获取table /// /// /// /// 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 } }