zoukankan      html  css  js  c++  java
  • PI Square中文论坛: PI SDK 开发中级篇| PI Square

    注: 为了更好的利用站内资源营造一个更好的中文开发资源空间,本文为转发修正帖,原作者为OSIsoft技术工程师王曦(Xi Wang),原帖地址:PI SDK 中级篇

     来源:https://d.gg363.site/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&ved=2ahUKEwjJ3-HLrP3eAhXJULwKHWSqBloQFjADegQIBxAB&url=https%3A%2F%2Fpisquare.osisoft.com%2Fcommunity%2Fall-things-pi%2Fchinese%2Fblog%2F2016%2F08%2F19%2Fpi-sdk-%25E5%25BC%2580%25E5%258F%2591%25E4%25B8%25AD%25E7%25BA%25A7%25E7%25AF%2587&usg=AOvVaw0su5H-KyXXiNMxqaueXYFa

    本帖旨在介绍使用PI SDK做一些基本的数据分析,同时,也包括了数据更新的方法,和一些推荐的程序结构。

    本帖针对已对PI SDK基础篇比较了解的开发人员。由于OSIsoft在.NET环境下的开发包,已基本由AF SDK取代,因此,本帖只使用C++语言作为PI SDK的开发平台。如果您需要在.NET环境中进行二次开发,请参考AF SDK中级篇。

    说明:PI SDK 是过时的技术

    1. 准备工作

    在这里的第一段程序,是推荐使用的,进行PI服务器的连接工作,是用子程序的调用方式:

     
    1. static ServerPtr PIServerConnect(_bstr_t servername)   
    2. {  
    3.     ::CoInitializeEx(NULL,COINIT_APARTMENTTHREADED);                  // 初始化COM              
    4.     IPISDKPtr spPISDK                                                 // 创建PI SDK连接  
    5.     spPISDK.CreateInstance(__uuidof(PISDK));                          // 实例化PI SDK连接  
    6.     ServerPtr spServer = spPISDK->GetServers()->GetItem(servername);  // 通过参数,获取连接  
    7.     return spServer;                                                  // 返回已连接的服务器指针  
    8. }  

    进行数据的基本分析,需要搜索PI服务器内的点的数据,以下两端子程序,来自PI基础篇的所有点名和搜索点表的子程序:

    按点名搜索:

     
    1. static PIPointPtr GetPIPointsByName(ServerPtr server, _bstr_t tagname)  
    2. {  
    3.     return server->PIPoints->GetItem(tagname);  // 返回一个PIPoint类型的指针  
    4. }  

    按点表搜索:

     
    1. static _PointListPtr SearchPIPoints(ServerPtr server, _bstr_t condition)  
    2. {  
    3.    return server->GetPoints(condition, NULL); //返回PointList指针类  
    4. }  

    2. Variant类型转换: 这是非常重要的部分,后面所有函数的讲解,都要依据此部分的功能

    PI SDK的大部分函数所需要的参数,都要转换成variant类型,有的传递variant指针,有的传递引用,有的传递二级指针。下面的转换工作将为您展示如何将字符串类型的指针转换成variant类型:

    在C++中,字符串指针一般会使用bstr指针类,我们使用这个类型作为例子,进行转换:

     
    1. _bstr_t start = "*-2h";                       // 字符串类起始时间  
    2. _variant_t starttime = (_variant_t)start;     // variant类起始时间  

    上面是比较简单的方法,直接做的指针类型强制转换。

    下面是做更加通用的方法:

     
    1. _bstr_t start = "*-2h";                                   //起始时间字符串  
    2. _PITimeFormatPtr spStart;                                 // 定义PI时间格式指针  
    3. spStart.CreateInstance(__uuidof(PITimeFormat));           // 指针实例化  
    4. spStart->InputString = start;                             // 指针指向起始时间字符串  
    5.   
    6.   
    7. VariantInit                                              // 初始化variant指针  
    8. V_VT (&starttime) = VT_DISPATCH;                         // 在variant内部类中进行通用指针转换  
    9. V_DISPATCH(&starttime) = spStart;                        // 使用dispatch函数,将variant指针指向PI时间格式指针  
    10. 除了PI的时间,PI的服务器名,PI点名等等,基本都是用这种方法进行格式转换。  

    有了这部分内容后,后面各个函数将省略参数类型转换的功能。

    功能一:取某一时间段的值(对应PI Datalink中的compressed data功能)

     
    1. static _PIValuesPtr CompressedData (PIPointPtr spPoint, _variant_t starttime, _variant_t endtime)  
    2. {      
    3.     return spPoint->Data->RecordedValues(&starttime, &endtime, BoundaryTypeConstants::btAuto, "", FilteredViewConstants::fvRemoveFiltered, NULL);    
    4. }  

    这个函数看似简单,但其中的参数需要说明:

    a. 参数starttime和endtime,都是variant &(引用)

    b. BoundaryTypeConstants和FilteredViewConstants分别对应的功能就是PI Datalink中的边界类型和标记过滤值的功能

    c. 比较不明显的,在参数中,有""参数,它代表的就是PI Datalink中的过滤条件,因为现在为测试,所以过滤条件在这里没有体现

    功能二:按标准时间间隔显示数据(对应PI Datalink中的采样数据)

     
    1. static _PIValuesPtr SampledData (PIPointPtr spPoint, _variant_t starttime, _variant_t endtime)  
    2. {      
    3.     return sampled->InterpolatedValues2(&starttime, &endtime, &vtinterval, "", FilteredViewConstants::fvRemoveFiltered, NULL);  
    4. }  

    此使用的参数与前面一个基本相同,只是多了一个&vtinterval,这个参数同样是variant的引用,意义是采样频率。

    功能三:数据计算,这部分使用数据在一段时间内,以一个采样频率求和的功能,其他的,类似最大值,最小值等,基本都是使用类似的方法

     
    1. void GetSummariesValues(PIPointPtr spPIPoint, _variant_t vtStart, _variant_t vtEnd, _bstr_t interval)  
    2. {  
    3.     IPIData2Ptr ipdata2 = (IPIData2Ptr)spPIPoint->Data;                         // 使用PIData2接口类指针  
    4.     _NamedValuesPtr summary = ipdata2->Summaries2(vtStart, vtEnd,interval, ArchiveSummariesTypeConstants::asTotal,CalculationBasisConstants::cbEventWeighted,NULL);  // 定义NamedValues指针类  
    5.     _variant_t reference = "Total";                                                              
    6.     VARIANT vt_Item = reference;                                                                 // 转换指针为引用  
    7.     NamedValuePtr total = summary -> GetItem(&vt_Item);  
    8.     spPIValues = (_PIValuesPtr)total->Value;  
    9. }  

    这个函数略微有点复杂,原因在于,需要计算的,如和,最大值,最小值,方差等的信息,都存在NamedValues指针类。同时,我们看到了variant指针和variant引用之间的转换方式。

    在NamedValue指针类中,使用summary函数,将给定PI点按照时间段和采样频率进行求和。

    功能四:取值

    刚才所有的功能,返回的值都是PIValues,也就是类似于一个数组,下面的功能是遍历这个数组中的每一个数:

     
    1. for(long i = 1; i <= spPIValues->Count; i++)  
    2. {  
    3.     _PIValuePtr spPIValue = spPIValues->GetItem(i);  
    4. }  

    这个做法很通俗,就不多讲了

    功能五:数据更新,在此,默认数据类型是浮点型32位

     

     
    1. HRESULThr = spPIPoint->Data->UpdateValues(spPIValues, DataMergeConstants::dmInsertDuplicates, NULL);  

    数据更新的功能是向PI服务器更新或插入数据,这个函数的使用需要比较小心。

    首先,在使用这个函数之前,接口与数据源的数据传递应符合数据源的数据传递协议。当数据到达快照之后,应先使用_PIValuePtr spPIValue = spPIPoint->Data->GetSnapshot()获取点的数据;之后使用

    spPIValues->put_ReadOnly(false)将PIValues指针类的写权限打开;然后,spPIValues->Add("*",spPIValue->Value.fltVal + 1,spNVValAttr)将刚才的值写入PIValues指针类;最后,spPIValues->put_ReadOnly(true)将只读打开。

    经过上述描述,相信大家已经明白数据更新的过程了。需要说明的是,PIValues指针类可以容纳很多的数据,也就是说,UpdateValues可以支持多点的同时更新。

    除了数据的插入,这个函数还可以用作数据替换。您可能已经注意到了dmInsertDuplicates这个参数,同样,如果这个参数被替换成:dmReplaceDuplicates,那么,实现的功能就是替换给定时间的数据。这个时间的设定,就是在spPIValues->Add("*",spPIValue->Value.fltVal + 1,spNVValAttr)中,“*” 表示当前时间,同样,可以使用具体的时间戳进行替换,不过必不可少的就是variant类型的转换。

     

    功能六:数据输出更新

    PI系统的数据传输更新用于向外发送数据,主要使用EVENTPIPE这个工具。如果使用之传输数据,要分两步走

    1. 创建EVENTPIPE

     
    1. static IEventPipe2Ptr Get_EventPipe (_PointListPtr spPointList)  
    2. {  
    3.     IEventPipe2Ptr spEventPipe2 = (IEventPipe2Ptr)spPointList->Data->EventPipe;       // 需要使用 IEventPipe2Ptr类型的指针,并且需要已经定义好的点表作为参数,用来明确需要哪些点的数据更新  
    4.     spEventPipe2->PollInterval = 2000;                                                // 数据更新频率,单位毫秒  
    5.     return spEventPipe2;                                                              // 返回这个指针  
    6. }  

    2. 获取数据:

     
    1. void GetValue_EventPipe (EventPipePtr spEventPipe)  
    2. {  
    3.     while (spEventPipe->Count > 0)  
    4.     {  
    5.         _PIEventObjectPtr spEventObject = spEventPipe->Take();             // 定义一个PIEventObject类型的指针,获取刚才定义好的EVENTPIPE中的数据  
    6.         PointValuesPtr spPointValue = spEventObject->EventData;            // 将这个数据传递给PointValues指针参数  
    7.     }  
    8. }  

    EVENTPIPE的作用就像一个队列,可以将不同点,不同时间的数据进行存储,当有客户端需要数据时,就把这些数据一次性直接给这个客户端。

    注释一:PI服务器的值,在C++中的处理

    PI中存储的值,在C++中是以variant类型存在的,因此,如果需要普通类型的值,可以使用如下的例子,这个例子是OSIsoft德国办公室资深工程师Andreas写的,您可以浏览他的博客,本贴只是加中文注释

    MyPIValue::MyPIValue (_PIValuePtr pv) {                                                                                            // 将PI的值指针传递进该类,并且对值指针中所包含的内容进行归类分解
           codtTimeStamp = pv->TimeStamp->LocalDate;                                                                                
           bstrTimeStamp = (_bstr_t)codtTimeStamp.Format(_T("%d-%b-%Y %H:%M:%S"));
           DigitalStatePtr tmpDigitalState = NULL;
           IDispatchPtr    tmpDispatch = NULL;
           _PITimePtr      tmpPITime = NULL;
           COleDateTime    tmpTS;
           HRESULT         hr = E_FAIL;

           _variant_t vT = pv->Value;                                                                                                              // 过去值指针中的点的数据
           vt = vT.vt;

           switch (vT.vt) {
           case VT_I4:                                                                                                                                      // variant VT_I4类存储的是整形32位
                  // Int32
                  intValue = vT.lVal;
                  dblValue = intValue;
                  bstrValue = (_bstr_t)intValue;
                  break;
           case VT_I2:                                                                                                                                      // variant VT_I2类存储的是整形16位
                  // Int16
                  intValue = vT.iVal;
                  dblValue = intValue;
                  bstrValue = (_bstr_t)intValue;
                  break;
           case VT_R8:                                                                                                                                    // variant VT_R8类存储的是浮点形64位
                  // Float64
                  dblValue = vT.dblVal;
                  intValue = (int)dblValue;
                  bstrValue = (_bstr_t)dblValue;
                  break;
           case VT_R4:                                                                                                                                    // variant VT_R4类存储的是浮点形32位
                  // Float16/Float32
                  dblValue = vT.fltVal;
                  intValue = (int)dblValue;
                  bstrValue = (_bstr_t)dblValue;
                  break;
           case VT_BSTR:                                                                                                                              // variant VT_BSTR类存储的是字符串类
                  // String
                  bstrValue = vT.bstrVal;
                  dblValue = 0;
                  intValue = 0;
                  break;
           case VT_DISPATCH:                                                                                                                      // variant VT_DISPATCH类存储的是数字类型,这是最复杂的
                  // Digital?                                                                                                                                   // 首先需要拿到数字类型表示的内容
                  tmpDispatch = vT.pdispVal;
                  hr =  tmpDispatch.QueryInterface(__uuidof(DigitalState),&tmpDigitalState);
                  if (hr == S_OK) {
                         bstrValue = tmpDigitalState->Name;
                         intValue = tmpDigitalState->Code;
                         dblValue = intValue;
                  }
                  // Timestamp?                                                                                                                           // 然后然后获取数字类型值的时间戳
                  hr =  tmpDispatch.QueryInterface(__uuidof(_PITime),&tmpPITime);
                  if (hr == S_OK) {
                               tmpTS = tmpPITime->LocalDate;
                               bstrValue = (_bstr_t)tmpTS.Format(_T("%d %B %Y %H:%M:%S"));
                               intValue = 0;
                               dblValue = 0;
                  }
                  break;
           default :
                  dblValue = 0.0;
                  intValue = 0;
                  bstrValue = "n/a";
                  break;
           }
    };

    注释二:后续工作---指针清空,关闭COM

    为了保证没有内存泄露的情况,在程序的最后,需要清空指针,还要进行:

     
    1. ::CoUninitialize();  

    用于关闭COM LIBARAY

    以上是各个功能模块的介绍,下面是一个用PI SDK进行求和工作的完整程序,也是推荐的程序结构方式:

     
    1. #include "stdafx.h"                                      
    2. #include <iostream>   
    3. #include <string>  
    4. #include "ATLComTime.h" //for COleDateTime  
    5.   
    6. #import "C:Program FilesPIPCPISDKPISDKCommon.dll" no_namespace  
    7. #import "C:Program FilesPIPCPISDKPITimeServer.dll" no_namespace  
    8. #import "C:Program FilesPIPCPISDKPISDK.dll" rename("Connected", "PISDKConnected") no_namespace  
    9. VOID WINAPI Sleep(_In_ DWORD dwMillisecons);                                                                                                                          // 以上为程序头文件  
    10.   
    11. class MyPIValue                                                                                                                                                                                       // 建立一个PIValue的默认类  
    12. {  
    13.     _PIValuePtr spPIValue;  
    14. public:  
    15.     MyPIValue (_PIValuePtr);  
    16.     double dblValue;  
    17.     int intValue;  
    18.     _bstr_t bstrValue;  
    19.     _bstr_t bstrTimeStamp;  
    20.     COleDateTime codtTimeStamp;  
    21.     VARTYPE vt;  
    22.   
    23. };  
    24.   
    25. MyPIValue::MyPIValue (_PIValuePtr pv) {                                                                                                                                               // 建立一个翻译PIValue的类  
    26.        codtTimeStamp = pv->TimeStamp->LocalDate;  
    27.        bstrTimeStamp = (_bstr_t)codtTimeStamp.Format(_T("%d-%b-%Y %H:%M:%S"));  
    28.        DigitalStatePtr tmpDigitalState = NULL;  
    29.        IDispatchPtr    tmpDispatch = NULL;  
    30.        _PITimePtr      tmpPITime = NULL;  
    31.        COleDateTime    tmpTS;  
    32.        HRESULT         hr = E_FAIL;  
    33.   
    34.        _variant_t vT = pv->Value;  
    35.        vt = vT.vt;  
    36.   
    37.        switch (vT.vt) {  
    38.        case VT_I4:  
    39.               // Int32  
    40.               intValue = vT.lVal;  
    41.               dblValue = intValue;  
    42.               bstrValue = (_bstr_t)intValue;  
    43.               break;  
    44.        case VT_I2:  
    45.               // Int16  
    46.               intValue = vT.iVal;  
    47.               dblValue = intValue;  
    48.               bstrValue = (_bstr_t)intValue;  
    49.               break;  
    50.        case VT_R8:  
    51.               // Float64  
    52.               dblValue = vT.dblVal;  
    53.               intValue = (int)dblValue;  
    54.               bstrValue = (_bstr_t)dblValue;  
    55.               break;  
    56.        case VT_R4:  
    57.               // Float16/Float32  
    58.               dblValue = vT.fltVal;  
    59.               intValue = (int)dblValue;  
    60.               bstrValue = (_bstr_t)dblValue;  
    61.               break;  
    62.        case VT_BSTR:  
    63.               // String  
    64.               bstrValue = vT.bstrVal;  
    65.               dblValue = 0;  
    66.               intValue = 0;  
    67.               break;  
    68.        case VT_DISPATCH:  
    69.               // Digital?  
    70.               tmpDispatch = vT.pdispVal;  
    71.               hr =  tmpDispatch.QueryInterface(__uuidof(DigitalState),&tmpDigitalState);  
    72.               if (hr == S_OK) {  
    73.                      bstrValue = tmpDigitalState->Name;  
    74.                      intValue = tmpDigitalState->Code;  
    75.                      dblValue = intValue;  
    76.               }  
    77.               // Timestamp?  
    78.               hr =  tmpDispatch.QueryInterface(__uuidof(_PITime),&tmpPITime);  
    79.               if (hr == S_OK) {  
    80.                            tmpTS = tmpPITime->LocalDate;  
    81.                            bstrValue = (_bstr_t)tmpTS.Format(_T("%d %B %Y %H:%M:%S"));  
    82.                            intValue = 0;  
    83.                            dblValue = 0;  
    84.               }  
    85.               break;  
    86.        default :  
    87.               dblValue = 0.0;  
    88.               intValue = 0;  
    89.               bstrValue = "n/a";  
    90.               break;  
    91.        }  
    92. };  
    93.   
    94.   
    95.   
    96. IPISDKPtr       spPISDK = NULL;            /* The PISDK */                                                                                              // 初始化所有需要用的指针  
    97. PISDKVersionPtr spSDKVersion = NULL;       /* PI SDK Version */  
    98. ServerPtr       spServer = NULL;           /* The Server */  
    99. PIPointPtr      spPIPoint = NULL;          /* The PI Point */  
    100. _PIValuePtr     spPIValue = NULL;   
    101. _PIValuesPtr     spPIValues = NULL;        /* The PI value */  
    102. _PITimeFormatPtr spStartTime = NULL;  
    103. _PITimeFormatPtr spEndTime = NULL;  
    104.   
    105. void GetSummariesValues(PIPointPtr spPIPoint, _variant_t vtStart, _variant_t vtEnd, _bstr_t interval)                                // 创建子函数  
    106. {  
    107.     IPIData2Ptr ipdata2 = (IPIData2Ptr)spPIPoint->Data;   
    108.     _NamedValuesPtr summary = ipdata2->Summaries2(vtStart, vtEnd,interval, ArchiveSummariesTypeConstants::asTotal,CalculationBasisConstants::cbEventWeighted  
    109.         ,NULL);  
    110.     _variant_t reference = "Total";  
    111.     VARIANT vt_Item = reference;  
    112.     NamedValuePtr total = summary -> GetItem(&vt_Item);  
    113.     spPIValues = (_PIValuesPtr)total->Value;  
    114.     for (long i = 1; i <= spPIValues->Count; i++)  
    115.     {  
    116.         spPIValue = spPIValues->GetItem(i);  
    117.         MyPIValue t(spPIValue);  
    118.         std::cout << t.bstrTimeStamp << " ";  
    119.         std::cout << t.bstrValue << std::endl;  
    120.     }  
    121.      total.Release();  
    122.      summary.Release();  
    123. }  
    124.   
    125.   
    126. int _tmain(int argc, _TCHAR* argv[])  
    127. {  
    128.        // Initialize COM  
    129.        ::CoInitializeEx(NULL,COINIT_APARTMENTTHREADED);  
    130.        // Check the command line switches  
    131.        if (argc < 6) {  
    132.               std::cout << "Command Line:" << std::endl  
    133.                         << (_bstr_t)argv[0] << " SERVERNAME TAGNAME starttime endtime interval";  
    134.               return (1);  
    135.        }  
    136.        try                                     
    137.        {  
    138.               // Create an instance of the PI SDK                                                                                              // 主函数中连接PI服务器,也可使用子函数调用的方式  
    139.               spPISDK.CreateInstance(__uuidof(PISDK));  
    140.               // Print out the PI SDK version  
    141.               spSDKVersion = spPISDK->PISDKVersion;  
    142.               std::cout << std::endl << "PI-SDK Version "  
    143.                         << spSDKVersion->Version << " Build "  
    144.                         << spSDKVersion->BuildID << std::endl;  
    145.               // get the PI Server  
    146.               spServer = spPISDK->GetServers()->GetItem((_bstr_t)argv[1]);                                                                    // 从输入参数1中获取PI服务器名  
    147.               spPIPoint = spServer->PIPoints->GetItem((_bstr_t)argv[2]);                                                                          // 从输入参数2中获取点名  
    148.               spStartTime.CreateInstance (__uuidof(PITimeFormat));  
    149.               spEndTime.CreateInstance (__uuidof(PITimeFormat));  
    150.               spStartTime->InputString = argv[3];                                                                                                                     // 从输入参数3中获取起始时间  
    151.               spEndTime->InputString = argv[4];                                                                                                                      // 从输入参数4中获取截止时间  
    152.               _bstr_t interval = argv[5];                                                                                                                                        // 从输入参数5中获取采样频率  
    153.   
    154.                 
    155.               _variant_t vtStart;  
    156.               VariantInit (&vtStart);  
    157.               V_VT (&vtStart) = VT_DISPATCH;  
    158.               V_DISPATCH(&vtStart) = spStartTime;  
    159.   
    160.               _variant_t vtEnd;  
    161.               VariantInit (&vtEnd);  
    162.               V_VT (&vtEnd) = VT_DISPATCH;  
    163.               V_DISPATCH(&vtEnd) = spEndTime;  
    164.   
    165.               GetSummariesValues(spPIPoint, &vtStart, &vtEnd, interval);  
    166.               // You can use more than just one tagname  
    167.               /*for (int ii = 2; ii< argc; ii++) { 
    168.                      // Tagname 
    169.                      std::cout << (_bstr_t)argv[ii] << std::endl; 
    170.                      spPIPoint = spServer->PIPoints->GetItem((_bstr_t)argv[ii]); 
    171.                      // Snapshot 
    172.                      spPIValue = spPIPoint->Data->Snapshot; 
    173.                      MyPIValue mPV(spPIValue); 
    174.                      std::cout << mPV.bstrTimeStamp << " "; 
    175.                      std::cout << mPV.bstrValue << std::endl; 
    176.               }*/  
    177.               V_VT (&vtStart) = VT_EMPTY;  
    178.               spStartTime.Release();  
    179.               V_VT (&vtEnd) = VT_EMPTY;  
    180.               spEndTime.Release();                                                                                                                                   // 以下为指针释放,整个程序中最需要注意的部分  
    181.   
    182.                
    183.   
    184.               spPIValue.Release();  
    185.               spPIValues.Release();  
    186.               spPIPoint.Release();  
    187.               spSDKVersion.Release();  
    188.               spPISDK.Release();  
    189.   
    190.        }  
    191.        catch( _com_error Err )  
    192.        {  
    193.               std::cout << "Error: "  
    194.                         << Err.Description()  
    195.                         << " : "  
    196.                         << Err.Error()  
    197.                         << std::endl;  
    198.               return (1);  
    199.        }  
    200.        Sleep(5000);  
    201.        return 0;  
    202. }  

  • 相关阅读:
    mysql修改数据表名
    HDU 5742 It's All In The Mind (贪心)
    HDU 5752 Sqrt Bo (数论)
    HDU 5753 Permutation Bo (推导 or 打表找规律)
    HDU 5762 Teacher Bo (暴力)
    HDU 5754 Life Winner Bo (博弈)
    CodeForces 455C Civilization (并查集+树的直径)
    CodeForces 455B A Lot of Games (博弈论)
    CodeForces 455A Boredom (DP)
    HDU 4861 Couple doubi (数论 or 打表找规律)
  • 原文地址:https://www.cnblogs.com/zouhao/p/10048149.html
Copyright © 2011-2022 走看看