|
@@ -0,0 +1,415 @@
|
|
|
|
+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(10000);
|
|
|
|
+ // 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());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /// <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
|
|
|
|
+ }
|
|
|
|
+}
|