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. }  

  • 相关阅读:
    (4) 编译 Android-5.0 源码
    (3) 下载 Android-5.0 源码
    (2) 搭建 Android 系统开发环境
    npm 安装 --save-dev 与 --save的使用与区别
    一点目标
    AcWing 875. 快速幂
    Codeforces Round #604 (Div. 2)
    2019年安徽大学ACM/ICPC实验室新生赛(公开赛)D 不定方程
    C语言黑与白问题
    AcWing 92. 递归实现指数型枚举
  • 原文地址:https://www.cnblogs.com/zouhao/p/10048149.html
Copyright © 2011-2022 走看看