Recently we got increasing interest in providing sample code in C++.
While C++ is a very powerful language you should be aware that .NET provides you with many little things that make your life as a developer much easier.
In the following post I will create a simple command line application that gets the snapshot of tags. The usage is:
PISDK_GetSnapshot <SERVERNAME> <Tagname1> <Tagname2> <…>
So as you may guess, we start with an empty C++ command line project:
Now let’s go to the code. First some includes:
#include "stdafx.h"
#include <iostream>
#include <string>
#include "ATLComTime.h" // for COleDateTime
Then we import the PI SDK libraries.
//need to import sdk
#import "E:\PIPC\PISDK\PISDKCommon.dll" no_namespace
#import "E:\PIPC\LIBRARY\PITimeServer.dll" no_namespace
#import "E:\PIPC\PISDK\PISDK.dll" rename("Connected", "PISDKConnected") no_namespace
One of the things that constantly drives me crazy when switching between C++ and C#.NET is handling the PIValue, especially the variant PIValue->Value – so the very first thing I’d like to do is introducing a simple class that does this for me:
class MyPIValue
{
_PIValuePtr spPIValue;
public:
MyPIValue (_PIValuePtr);
double dblValue;
int intValue;
_bstr_t bstrValue;
_bstr_t bstrTimeStamp;
COleDateTime codtTimeStamp;
VARTYPE vt;
};
The purpose of my simple class is to get variables that handle much better than the variant (at least in my simple sample code snippets).
Now let’s go to the constructor. Actually we need to figure where to find the data – and that is derived by the variant type. The following code gets me first a COleDateTime and a string representation of that – this is the timestamp, and later the value part as integer, double or string representation:
MyPIValue::MyPIValue (_PIValuePtr pv) {
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:
// Int32
intValue = vT.lVal;
dblValue = intValue;
bstrValue = (_bstr_t)intValue;
break;
case VT_I2:
// Int16
intValue = vT.iVal;
dblValue = intValue;
bstrValue = (_bstr_t)intValue;
break;
case VT_R8:
// Float64
dblValue = vT.dblVal;
intValue = (int)dblValue;
bstrValue = (_bstr_t)dblValue;
break;
case VT_R4:
// Float16/Float32
dblValue = vT.fltVal;
intValue = (int)dblValue;
bstrValue = (_bstr_t)dblValue;
break;
case VT_BSTR:
// String
bstrValue = vT.bstrVal;
dblValue = 0;
intValue = 0;
break;
case 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;
}
Preparation doneJ!
As mentioned in the beginning, I want to get the snapshot – so what do I need? The PISDK, the Server, the Point, the Value and just for the fun the PISDK Version J:
IPISDKPtr spPISDK = NULL; /* The PISDK */
PISDKVersionPtr spSDKVersion = NULL; /* PI SDK Version */
ServerPtr spServer = NULL; /* The Server */
PIPointPtr spPIPoint = NULL; /* The PI Point */
_PIValuePtr spPIValue = NULL; /* The PI value */
Now the code – we initialize COM, check for the command line parameters and finally create the PISDK. After this has been done, we print out the PI SDK version, connect to PI and print out the snapshot of all tags provided as command line arguments:
int _tmain(int argc, _TCHAR* argv[])
{
// Initialize COM
::CoInitializeEx(NULL,COINIT_APARTMENTTHREADED);
// Check the command line switches
if (argc < 3) {
std::cout << "Command Line:" << std::endl
<< (_bstr_t)argv[0] << " SERVERNAME TAGNAME(s)";
return (1);
}
try
{
// Create an instance of the PI SDK
spPISDK.CreateInstance(__uuidof(PISDK));
// Print out the PI SDK version
spSDKVersion = spPISDK->PISDKVersion;
std::cout << std::endl << "PI-SDK Version "
<< spSDKVersion->Version << " Build "
<< spSDKVersion->BuildID << std::endl;
// get the PI Server
spServer = spPISDK->GetServers()->GetItem((_bstr_t)argv[1]);
// You can use more than just one tagname
for (int ii = 2; ii< argc; ii++) {
// Tagname
std::cout << (_bstr_t)argv[ii] << std::endl;
spPIPoint = spServer->PIPoints->GetItem((_bstr_t)argv[ii]);
// Snapshot
spPIValue = spPIPoint->Data->Snapshot;
MyPIValue mPV(spPIValue);
std::cout << mPV.bstrTimeStamp << " ";
std::cout << mPV.bstrValue << std::endl;
}
}
catch( _com_error Err )
{
std::cout << "Error: "
<< Err.Description()
<< " : "
<< Err.Error()
<< std::endl;
return (1);
}
return 0;
}
And here is the result:
This was the first post - stay tuned for more examples using C++!
Nice, thanks for posting Andreas! You might want to change your variable "i" to "j" or "ii" in your for loop... otherwise you get light bulb icons in the post ;)