使用C#进行基于PI的开发

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用C#进行基于PI的开发相关的知识,希望对你有一定的参考价值。

 

首先欲基于PI-SDK开发PI数据库,必须先安装PI-SDK,然后再VS中新建了一个aspnet网站,添加引用:PI-SDK 1.2 Type Library 和 PISDKCommon 1.2 Type Library

花了很多时间在PISDK下面找了一个能实例化的类PISDKClass,后来又发现原来Servers 是这个对象的一个属性.

private PISDK.IPISDK piSDK= new PISDK.PISDKClass();

private PISDK.Server _piServer;

private PISDK.PIPoint _piPoint;

在Page_Load事件枚举所有的数据源,添加到DropSownlist.

foreach(PISDK.Server item in piSDK.Servers)

{

DropDownList1.Items.Add(srv.Name);

}

设置_piServer为dropdown里面选中的数据库

_piServer = piSDK.Servers[DropDownList1.Text];

然后下面接着防置一个文本框来读取用户输入的Tag点,Tag有点相当于普通数据库的字段.但是又不一样,关于这个暂时不细说了

和一个ListBox来得到这个Tag的所有属性.代码如下:

_piPoint = _piServer.PIPoints[tbxTagName.Text];

ListBox1.Items.Clear();

foreach (PISDK.PointAttribute ptatr in _piPoint.PointAttributes)

{

ListBox1.Items.Add(ptatr.Name);

}

读取Tag属性的值可以用下述属性.

tbxAttValue.Text = _piPoint.PointAttributes[ListBox1.Text].Value.ToString();

如果要更新这个属性的值呢?仍然很简单

PISDK.PointAttribute ptatr;

_piPoint = _piServer.PIPoints[tbxTagName.Text];

ptatr = _piPoint.PointAttributes[lblAttrName.Text];

_piPoint.PointAttributes.ReadOnly = false;

ptatr.Value = tbxAttValue.Text;

_piPoint.PointAttributes.ReadOnly = true;

blNotion.Text = "更新成功!";

其实最后一句想用MessageBox可惜在asp.net下面一直找不到好的使用MessageBox的方法,原来在CodeProject上面找了一个,但是因为后来发现在Atlas的updatepanel中不能使用,也就作罢了

OK,上面就是我根据SDK-Manual里面的一个Basic例子,用C#改写了一下,实现了和例子一样的功能,虽然今天花费了

下面是一个简单的截图和SDK截图的对比:

首先欲基于PI-SDK开发PI数据库,必须先安装PI-SDK,然后再VS中新建了一个aspnet网站,添加引用:PI-SDK 1.2 Type Library 和 PISDKCommon 1.2 Type Library

接着看文档上面VB的例子代码是

Option Explicit

Dim srv as Server

Dim pt As PIPoint

for Each srv In Servers

Combo1.AddItem srv.Name

Next srv

为了把这两个地方改成能运行得C#代码还花费了不少力气,因为原来没有做的东西都是很简单的.net东西,基本上没有用到过和COM组件进行交互.首先说什么不能实例花,接着又说什么Servers不行,花了很多时间在PISDK下面找了一个能实例化的类PISDKClass,后来又发现原来Servers 是这个对象的一个属性.

private PISDK.IPISDK piSDK= new PISDK.PISDKClass();

private PISDK.Server _piServer;

private PISDK.PIPoint _piPoint;

在Page_Load事件枚举所有的数据源,添加到DropSownlist.

foreach(PISDK.Server item in piSDK.Servers)

{

DropDownList1.Items.Add(srv.Name);

}

设置_piServer为dropdown里面选中的数据库

_piServer = piSDK.Servers[DropDownList1.Text];

然后下面接着防置一个文本框来读取用户输入的Tag点,Tag有点相当于普通数据库的字段.但是又不一样,关于这个暂时不细说了

和一个ListBox来得到这个Tag的所有属性.代码如下:

_piPoint = _piServer.PIPoints[tbxTagName.Text];

ListBox1.Items.Clear();

foreach (PISDK.PointAttribute ptatr in _piPoint.PointAttributes)

{

ListBox1.Items.Add(ptatr.Name);

}

读取Tag属性的值可以用下述属性.

tbxAttValue.Text = _piPoint.PointAttributes[ListBox1.Text].Value.ToString();

如果要更新这个属性的值呢?仍然很简单

PISDK.PointAttribute ptatr;

_piPoint = _piServer.PIPoints[tbxTagName.Text];

ptatr = _piPoint.PointAttributes[lblAttrName.Text];

_piPoint.PointAttributes.ReadOnly = false;

ptatr.Value = tbxAttValue.Text;

_piPoint.PointAttributes.ReadOnly = true;

blNotion.Text = "更新成功!";

其实最后一句想用MessageBox可惜在asp.net下面一直找不到好的使用MessageBox的方法,原来在CodeProject上面

1>应用SDK从PI数据库读取数据

上一次的例子是通过PI-SDK获取Tag的属性并且修改它,我想更重要的是怎么取出Tag的值和进行更新吧。在进行尝试之前看了看文档,了解了一下PISDK的对象结构,下面这一张就是PISDK的结构图

 

简要了解在例子中要用到的对象,并把它们的描述进行了简单的翻译,我想翻译的目的应该是帮助自己更好的进行学习,给出英文对照。不妥之处,忘大牛们指正:

 

//**********************正文开始*************************************************/

The Server object represents a single server (data archive). Server objects are retrieved from the Servers collection and provide access to the basic entities represented on each server, such as points, point classes, digital states, users, and groups. Some basic properties are available from the Server object as initially retrieved from the Servers collection.

    Typically a program will require a network connection to the server. Connections are established either by calling the Open method of the Server object explicitly or by accessing a property or calling a method of the Server object that requires server access, forcing an implicit connection.

    Many of the services available through the Server object are accessed through its properties which return other objects. For example, the PIPoints property provides a PIPoints collection object for accessing the various points on a server.

    服务器(Server)对象表示了一个单独的PI数据库服务器,服务器对象可从服务器集合中获得并且提供了访问每个数据库中的实体的方法,这些实体包括点,点集合,数字量(开关量),用户和组。服务器对象提供了一些基本的属性从而在服务器集合中队服务器进行初始化。

      一般地,程序连接到服务器需要网络连接属性,这个连接可以通过服务器对象的Open方法显式的进行,也可以通过服务器对象的某个属性或方法连接需要验证的服务器来进行一个隐式的连接。

上面这一段有点不太懂,直到目前我都没有用过显式的Open方法,都是通过

    _piServer = piSDK.Servers.DefaultServer;

    这种所谓的隐式方法来连接,这个是不是就是所谓的隐式连接?因为在PISDK安装的时候指定了一个PI数据源,所以DefaultServer就是这个?所以我访问的时候都不需要输入用户或者密码

       我们可以通过服务器对象的属性来返回其他的对象从而获得很多服务。例如,通过服务器对象的PIPoints属性返回的PIPoints集合的对象来访问数据库里面的不同的点。

 

A PIConstant collection maps display strings to PI-SDK enumerations. This provides a way for an application to display strings indicating choices of behavior for a user to select particular actions. The application then retrieves the associated constant and passes it to a particular method being called. A PIConstant collection contains members, stored and accessed as NamedValue objects that represent the display string (the name) and the constant (the value). A PIConstant collection is retrieved using PISDK.PIConstants.Item.

       PIConstant集合映射字符串到PI-SDK枚举。(使字符串和枚举的项一一对应)。它为程序提供了字符串来代表用户选择的一个特殊的操作,然后程序可以把这个字符串对应的值获得并且把它传送到使用的方法里面去。PIConstant集合包含了Members(不知道什么意思),存贮和访问NamedValue对象。NamedValue对象是一个字符串(名字)-常量(数值)对。PIConstant集合中的对象通过PISDK.PIConstants.Item来获取

       我觉得这个PIConstant集合就是相当于在C#中用DialogueResult枚举中用OK代表某个具体的意思。具体代表的东西我们不用关心,知道是什么就可以了。NamedValue就是一个字符串-常量对,有点像一个字典吧,最后一句话说PISDK.PIConstants.Item来获取PIContant,但是我在C#中PISDK.PIConstants没有Item属性,反而使直接通过PISDK.PIConstants[Name]取到。同时NamedValue是在PISDKCommon命名空间下面

       The PIPoint object represents a point on a PI Server. The PIPoint is the primary access point to data on the Server as well as configuration of a point‘s properties. 

       PIPoint对象表征PI数据库里面的点,PIPoint是访问数据库里面点的值和属性的主要方法。

       The PIData object is associated with a single PIPoint and is used to send and retrieve values to and from the server. It is accessed through the Data property of the PIPoint.

       PIData对象是和单独的PIPoint联系的,它从服务器获取数据或者写回数据到服务器。它通过PIPoint的Data属性得到。

    我们知道PI数据库的数据分别存储在Snapshot或者Archive中,一个是快照一个是档案文件,这样做是为了方便PI数据库对数据进行压缩.那么自然对数据库的读取也分为对Snapshot和Archive读取.snapshot和archive的值都是用PIValue的形式表示的, PIValue对象包括了数值和时间。应用SDK从数据库中获取snapshop或者archive的值很简单。

    首先连接数据库,声明一个PIServer:_piServer = piSDK.Servers.DefaultServer;

    然后利用PIServer的Points属性访回一个点的集合,再通过tagName来获取点:PIPoint pt = _piServer.PIPoints[tbxTagName.Text];

    如果是使用Snapshot,直接声明一个PIValue通过PIPoint的Snapshot属性来返回值:pv = pt.Data.Snapshot;

    然后我们就可以通过PIValue的Value属性和TimeStamp属性来获取snapshot的值和该值对应的时间了。

        tbxValue.Text = pv.Value.ToString();

        tbxTime.Text = pv.TimeStamp.LocalDate.ToString();

    如果要读取Archive的值,那么要稍微麻烦一点,应为你要给定读取的时间和模式,PIPoint对象的ArcValue方法是用来获取Archive的值,帮助的说明如下:

        object.ArcValue TimeStamp, Mode, AsynchStatus

    这个应该是VB的语法吧,我们可以看出他有三个参数。第一个是时间,C#中我们可以直接传一个DateTime格式的数据进去,当然你也可以使用 PITime对象表示的时间,或者一个字符串。Mode代表取出的模式,因为PI数据库并不是每个时间都存有数据的,所以在你可以选择模式是读取你输入时间的当前点,后面一个点,前面一个点或者插值表示的时间。最后一个参数不解,文档里面没有说明,VB的例子则直接没有传这个参数,应该是一个可选参数,我的处理方法如下。

        RetrievalTypeConstants rtType;

        rtType = (RetrievalTypeConstants)piSDK.PIConstants["RetrievalTypeConstants"][cbxValue.Text].Value;

        pv = pt.Data.ArcValue(dt, rtType, new PISDKCommon.PIAsynchStatus());

    RetrievalTypeConstants 就是表示存取模式的,我这里使用过一个下拉列表框获得可用的模式的,最后一个参数是通过传一个新的实例下去,这里不知道这样和不合理,反正可以读出数据我就没有管了,又没有达人解释一下怎么处理这种VB的可选参数的情况呢?我学着VSTS的那种Type.Missing又不可以。

    然后读出来的PIValue同样包含一个数值和一个时间,现在我想大家应该理解为什么PIValue里面要包含一个时间了吧。

    我觉得应用SDK很简单,感觉和开发Excel很象,都是调用Com,也挺符合.net的开发习惯的,文档也好,每个对象还有一个详细的VB例子,虽然不是.net的,但是也可以大致了解得差不多。

 

2>应用SDK向PI数据库写入数据

    在PIData对象中有一个UpdateValue方法和UpdateValues方法,顾名思义,一个是更新单个数据,一个是更新一批数据。但是文档里面红红的标着Not Implemented两个单词,心顿时凉了,考虑到文档的版本比我实际使用的SDK版本第一点,去Object Browser里面搜索了一下,果然找到了这个方法

       public virtual void UpdateValue(object newValue, object TimeStamp, PISDK.DataMergeConstants MergeType, PISDKCommon.PIAsynchStatus asynchStatus)

    这些参数的意思没有文档也挺好理解的,这里就不说了,可是当我尝试用这个方法去更新数据库的时候,一样抛出了Not Implemented的错误,当时心里非常的郁闷,这个也就是开头我说到的麻烦。我的SDK的版本是1.2.0,问了一个比较有经验的人,他说他没有遇到过不能写入的问题,等有时间去问问他,看看是不是版本的问题,如果有人能写入的,请给我说一下你们的版本;

 

 

3>应用API从PI数据库读取数据

    SDK不行,还好我们还有PI-API,这个感觉和Win32 API挺象的吧,都是一写用C写的函数,PISDK其实就是把PIAPI包装了一下,可惜我对P/Invoke不怎么熟悉,如果大家对这个也不熟悉的话,我觉得这两篇文章还是不错,作为预备知识。

    在C#中通过P/Invoke调用Win32 DLL

    如何在C#中使用Win32和其他库

    这两篇文章看了我还是颇有收获的,至少知道引用类型在传递的时候不需要ref关键字,因为在.net中本身就是按地址传递的,呵呵,可能是基础知识太差。

    不过看API的文档和SDK的文档相比真是天壤之别,API文档只有一个简单的概述,例子也没有,参数的说明更是往往只有一句话,不过还好吧,我慢慢的试还是试出来了一些。

    对于数据读取和写入还是分为snapshot和archive,就是两个不同的函数,参数也差不多类似。

    PI-API函数是按组分开的,有Point函数,Archive函数和snapshot函数等。

    从archive数据库读取数据有两个,一个是扩展函数,功能多一些,所以我们在这里使用的是扩展函数来读取数据,其文档的声明如下:     

        int32 PIPROC piar_getarcvaluex(

      int32 ptnum,

        int32 mode,

      float64 PIPTR *drval,

      int32 PIPTR *ival,

      void PIPTR *bval,

      uint32 PIPTR *bsize,

        int32 PIPTR *istat,

      int16 PIPTR *flags,

        PITIMESTAMP PIPTR *time);

    int32,uint32这些都是PIAPI里面自己定义的类型,从名字可以很简单的看出他们对应32位整数,32位无符号整数,在C#中这些,.net已经给我们定制好了,还比较方便。

    PITIMESTAMP 是一个结构体,定义很简单,我们直接在C#中定制一个类似的结构就好了。

    ptnum是PI tag号对应的一个Int32位的整数来代表数据库中的一个点,可以通过API函数pipt_findpoint把Tag名字转换成ptnum。

    mode和SDK里面的模式很类似,就是看是读取前面一个点,还是后面一个点的值。

    如果读取出来的是浮点值,drval就是这个值;如果读取出来的是整型值,ival就是这个值;bval是一个字符串,bsize是这个字符串的缓冲区大小,istat是代表数值量的值,flags文档上面的说明是

Data quality flag mask不解。time是值对应的时间,相当于SDK里面的pv.TimeStamp.LocalDate。

    对于字符串,这里我看了半天,因为string类型是一个不定长的字符串,所以是不能用在这个地方的,这个地方的bval应该是使用 StringBuilder,StringBuilder.Capacity就表示了bsize,这一点还是冲上面那两篇文章看到的,真是基础差,呵呵。

    对于void指针,我是把它处理成为object类型,因为我们基本上不用到字符串的值,所以这里我也没有测试,达人指导一下,呵呵。综上,我的C#格式的函数是

        [DllImport("piapi32.dll", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]

        public static extern Int32 piar_getarcvaluex(

            Int32 ptnum,

            Int32 mode,

            ref Double drval,

            ref Int32 ival,

            [MarshalAs(UnmanagedType.AsAny)] object bval,

            ref UInt32 bsize,

            ref Int32 istat,

            ref Int16 flags,

            ref PITime.PITIMESTAMP time);

    第五个参数必须Mashall成AsAny类型,要不直接使用object类型是不行的。那么我怎么知道他是Any类型呢?因为文档上面VB的函数调用声明是bVal As Any,这也是一点灵感吧。至于返回的Int32值是代表调用是否成功的,就和win32 API类似吧。

以上是关于使用C#进行基于PI的开发的主要内容,如果未能解决你的问题,请参考以下文章

树莓派更新以后pi账户无法登陆密码都是默认的,求切实可行的解决办法

C# 使用大数组内存溢出的解决办法

Rock Pi开发笔记(三):Rock Pi 4B plus(基于瑞星微RK3399)板子硬件资源介绍

c#中字符串显示上标和下标解决办法

C#内存地址和变量

C++基础之宏