一、SCADA、PISDK是什么?
SCADA系统是数据采集与监控系统。SCADA系统现在主要应用在电力、冶金、石油、化工、等行业。
PISDK是PI数据库的二次开发工具,主要提供数据采集的二次开发的API。
SCADA系统采集到的数据都直接存入PI数据库,我们要想利用这些数据统计、应用、监控设备等,我就需要快速的把数据从PI数据库拿出来。
二、功能实现思路
PISDK提供了很多的二次开API,我们就利用PISDK提供的AIP接口实现快采集,在这里我想对不了解的PISDK小白们说其实任何一个经历很久的产品它的功能已经很强大了,你不用担心它不能满足你的需求。小菜我在接到项目经理安排的SCADA数据采集提速问题之后,也是走很多弯路,在网上到处找解决方案,找了很多解决方案示例!这些事例对小菜我来说太复杂,很难简单使用起来。小菜我当时在PISDK这么成熟的产品不肯能没有快速采集接口吧!于是我就详细的读了PISDK帮助文档就发现了我想要的解决方案。现在很多人不喜欢从头仔细的看文档,也没有书写文档的好习惯。小菜我提醒自己和大家要认认真的去看文档和写文档你会受益匪浅的。
废话不多说了开始进入正题。
PISDK提供了一套监控数据点时时变化的方案,次方案是先告诉PI数据库我要监控哪些点,之后你就可以从PISDK提供的数据出口AIP就能取到时时变化的数据了。
更多PISDK API请认真读帮助文档!
直接看代码,很容易实现
下载示例地址 C#_PISDK快速高效率采集SCADA数据点
using System; using System.Collections; using System.Data; using System.Data.SqlClient; using System.IO; using System.Text; using System.Windows.Forms; using PISDK; using PISDKCommon; using System.Runtime.InteropServices; using System.Threading; namespace PI_Edit { public partial class Form1 : Form { private PISDK.PISDK piSDK; // 定义PISDK接口piSDK private Server server; // 定义Server接口server private PISDK.PIValue piValue; private PISDK.ListData listData; private PointList pilist = new PointList(); private PointValues values; private EventPipe eventpipe; private PIValues pivalues; private PIValue pv; private PIAsynchStatus piassynch; private PISDK._DEventPipeEvents_Event EventNew; private PISDK.EventPipe mdbEvents; private static string Errorpath = AppDomain.CurrentDomain.BaseDirectory + "Errorlog.txt"; /// <summary> /// 构造函数 /// </summary> public Form1() { InitializeComponent(); piSDK = new PISDKClass(); // 创建PISDKClass对象,并使接口piSDK指向它 // 对ListBox控件lstServer进行初始化,使其列出服务器列表中的所有服务器名 foreach (Server srv in piSDK.Servers) { lstServer.Items.Add(srv.Name); } // 使lstServer控件的选中项为默认服务器名 lstServer.SelectedItem = piSDK.Servers.DefaultServer.Name; // 使PI用户为默认服务器的默认用户 txtUser.Text = piSDK.Servers.DefaultServer.DefaultUser; // 使接口server指向默认服务器 server = piSDK.Servers[lstServer.SelectedItem.ToString()]; comboBox1.Items.Add("Eventpoint"); comboBox1.Items.Add("pointValues"); comboBox1.SelectedIndex = 0; textBox1.Text = "1000"; } private void btnConnect_Click(object sender, EventArgs e) { DataTable table = new DataTable(); if (comboBox1.SelectedItem.ToString() == "Eventpoint") { ADDEventpoint(table); timer1.Tick -= new EventHandler(getpointvalues); timer1.Tick -= new EventHandler(getEventpipe); timer1.Tick += new EventHandler(getEventpipe); } if (comboBox1.SelectedItem.ToString() == "pointValues") { ADDPointValues(table); timer1.Tick -= new EventHandler(getEventpipe); timer1.Tick -= new EventHandler(getpointvalues); timer1.Tick += new EventHandler(getpointvalues); } } /// <summary> /// 取时时数据没有重复的点号,指定几个就是几个 PointValues取法 /// </summary> /// <param name="table"></param> private void ADDPointValues(DataTable table) { OpenPIServer(); DateTime startTime = DateTime.Now; foreach (DataRow Row in table.Rows) { try { PIPoint pt = server.PIPoints[Row[0].ToString()]; // 建立数据点监控列表,用于通知PI数据库 pilist.Add(pt); } catch (Exception e) { writerlog(e.Message); continue; } } DateTime EndTime = DateTime.Now; TimeSpan ts = EndTime - startTime; writerlog(string.Format("耗时:{0}", ts.ToString())); NamedValues nvsAttrs = new NamedValues(); listData = pilist.Data; values = listData.get_Snapshot(out nvsAttrs); //通知PI数据 //MessageBox.Show("添加Tag成功"); getpointvalues(null, null); button1_Click(null, null); } /// <summary> /// 添加时时值 Eventpoint取法,但是数据用重复 /// </summary> /// <param name="table"></param> private void ADDEventpoint(DataTable table) { OpenPIServer(); DateTime startTime = DateTime.Now; foreach (DataRow Row in table.Rows) { try { PIPoint pt = server.PIPoints[Row[0].ToString()]; pilist.Add(pt); } catch (Exception e) { writerlog(e.Message); continue; } } DateTime EndTime = DateTime.Now; TimeSpan ts = EndTime - startTime; writerlog(string.Format("耗时:{0}", ts.ToString())); NamedValues nvsAttrs = new NamedValues(); #region 取时时之 nvsAttrs.Add("Questionable", true); nvsAttrs.Add("Substituted", true); nvsAttrs.Add("Annotated", true); pilist.Data.RetrievalAttributes = nvsAttrs; pilist.Data.EventPipe.MaxCount = table.Rows.Count; eventpipe = pilist.Data.EventPipe; #endregion getEventpipe(null, null); button1_Click(null, null); } /// <summary> /// 添加pivalue 取值一个点的历史数据速度很快 /// </summary> /// <param name="table"></param> private void ADDvalues() { OpenPIServer(); DateTime startTime = DateTime.Now; try { PIAsynchStatus st = new PIAsynchStatus(); PIData pt = server.PIPoints[Tbtagid.Text.Trim()].Data; pivalues = pt.PlotValues(this.startTime.Text.Trim(), this.EndTime.Text.Trim(), 50, st); } catch (Exception e) { writerlog(e.Message); } DateTime EndTime = DateTime.Now; TimeSpan ts = EndTime - startTime; writerlog(string.Format("耗时:{0}", ts.ToString())); MessageBox.Show("添加Tag成功"); getOldvalue(null, null); } /// <summary> /// pointvalus 数据取法每次都是指定的几个点 /// </summary> private void getpointvalues(object sender, EventArgs e) { string path = AppDomain.CurrentDomain.BaseDirectory + "SCAdAloglog.txt"; DataTable tagTable = new DataTable(); tagTable.Columns.Add("Tag"); tagTable.Columns.Add("TagValue"); tagTable.Columns.Add("TagDataTime"); DateTime startTimezong = DateTime.Now; writerlog(string.Format("添加的:{0};取到的数据集合{1},", pilist.Count, values.Count)); foreach (PointValue value in values) { DataRow Row = tagTable.NewRow(); string TagName = value.PIPoint.Name; string TagValue = value.PIValue.Value.ToString(); string TagDateTime = value.PIValue.TimeStamp.LocalDate.ToString(); Row["Tag"] = TagName; Row["TagValue"] = TagValue; Row["TagDataTime"] = TagDateTime; tagTable.Rows.Add(Row); } DateTime EndTimezong = DateTime.Now; TimeSpan ts = EndTimezong - startTimezong; writerlog(string.Format("数据点总用时间:开始时间:{0};结束时间:{1};总用时间:{2};数据入库情况:{3}", startTimezong.ToString(), EndTimezong.ToString(), ts.ToString())); listBox1.Items.Add("拉去数据成功"); } /// <summary> /// EventPipe 取法 每次返回有变化的点 但是每一个点事双份的 /// </summary> private void getEventpipe(object sender, EventArgs e) {/*取时时值,但是没有一个点的数据时爽分的 速度可以 每秒1000条记录 */ string path = AppDomain.CurrentDomain.BaseDirectory + "SCAdAloglog.txt"; DataTable tagTable = new DataTable(); tagTable.Columns.Add("Tag"); tagTable.Columns.Add("TagValue"); tagTable.Columns.Add("TagDataTime"); DateTime startTimezong = DateTime.Now; writerlog(string.Format("添加的list={0};取到的数据集合{1},", pilist.Count, eventpipe.Count)); for (int i = 1; i <= eventpipe.Count; i++) { DataRow Row = tagTable.NewRow(); PointValue pvalue = (PointValue)eventpipe.Peek(i).EventData; string TagName = pvalue.PIPoint.Name; string TagValue = pvalue.PIValue.Value.ToString(); string TagDateTime = pvalue.PIValue.TimeStamp.LocalDate.ToString(); Row["Tag"] = TagName; Row["TagValue"] = TagValue; Row["TagDataTime"] = TagDateTime; tagTable.Rows.Add(Row); } DateTime EndTimezong = DateTime.Now; TimeSpan ts = EndTimezong - startTimezong; writerlog(string.Format("数据点总用时间:开始时间:{0};结束时间:{1};总用时间:{2};数据入库情况:{3}", startTimezong.ToString(), EndTimezong.ToString(), ts.ToString())); listBox1.Items.Add("拉去数据成功"); } /// <summary> /// pivalue 取发 有点慢 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void getOldvalue(object sender, EventArgs e) {/*取历史值 取时时值,但是没有一个点的数据时爽分的 速度可以 */ Guid id = Guid.NewGuid(); string path = AppDomain.CurrentDomain.BaseDirectory + "SCAdA" + id.ToString() + "loglog.txt"; DateTime startTimezong = DateTime.Now; writerlog(string.Format("添加的list={0};取到的数据集合{1},", pilist.Count, pivalues.Count)); foreach (PIValue value in pivalues) { DateTime startTime = DateTime.Now; string TagName = value.ToString(); string TagValue = value.Value.ToString(); string TagDateTime = value.TimeStamp.LocalDate.ToString(); DateTime EndTime = DateTime.Now; TimeSpan Tagst = EndTime - startTime; writerlog(string.Format("数据点名称&{0}&数据点值&{1}&数据点时间&{2}&数据点耗时&{3}", TagName, TagValue, TagDateTime, Tagst)); } DateTime EndTimezong = DateTime.Now; TimeSpan ts = EndTimezong - startTimezong; writerlog(string.Format("数据点总用时间:开始时间:{0};结束时间:{1};总用时间:{2}", startTimezong.ToString(), EndTimezong.ToString(), ts.ToString())); listBox1.Items.Add("拉去数据成功" + id.ToString()); } /// <summary> /// 打开PI数据库连接 /// </summary> private void OpenPIServer() { if (!server.Connected) { writerlog("pointvalue取数据开始:{0}"); writerlog("PI数据库连接状态" + server.Connected.ToString()); string connectString = string.Format("UID={0};PWD={1}", "***", "***"); //PI数据库登录账号密码 server.Open(connectString); //打开PI数据库连接,PI数据库是时时连接不需要关闭连接 writerlog("连接PI数据库情况:" + server.Name + " " + server.CurrentUser + " " + server.LastConnectedTime); writerlog("PI数据库连接状态" + server.Connected.ToString()); } } /// <summary> /// 记录日志文件 /// </summary> /// <param name="message">日志信息</param> private void writerlog(string message) { using (StreamWriter writer = new StreamWriter(Errorpath, true)) { writer.WriteLine("日志时间:" + DateTime.Now.ToString()); writer.WriteLine(message); writer.WriteLine(); } }
} }