ナビゲーションをスキップする
All Places > All Things PI - Ask, Discuss, Connect > Japan PI Square > ブログ > 2016 > September
2016

Japan PI Square

September 2016 先月 来月

PI AF SDKPI AFPI Data Archiveに直接接続するメソッドを提供しています。

PI Data Archiveに高速に書き込み処理を行う場合などにはPI Data Archiveに直接することは大変便利な機能です。

PI Data Archiveに直接接続するには.Net4を使用する必要があります)

 

アプローチ

  1. Visual Studioを開きます
  2. 新規プロジェクトを作成し、Visual C# > Console Applicationを選択し、名前を「AccessPI」とします。
  3. PI AF SDKの参照をプロジェクトに追加します
    1. プロジェクト > 参照の追加...
    2. アセンブリ > 拡張
    3. OSIsoft.AFSDKにチェックを付けます。バージョン4.0AFSDKにチェックをつけるようにしてください
    4. OKをクリックします
  4. using」の宣言をコード上部に追加します。
using OSIsoft.AF.PI;
using OSIsoft.AF;
using OSIsoft.AF.Time;
using OSIsoft.AF.Asset;

   5.default PI Data Archiveに接続します

PIServers myPIServers = new PIServers();
PIServer myPIServer = myPIServers.DefaultPIServer;
myPIServer.Connect();

   6.SinusoidPI Pointを探します

PIPoint pt = PIPoint.FindPIPoint(myPIServer, "sinusoid");   

   7.Current valueを取得します

AFValue Snapshot = pt.CurrentValue();
Console.WriteLine("Snapshot from Sinusoid: " + Snapshot.Value + "\t" + Snapshot.Timestamp.LocalTime);

   8.1日前から現在までのrecorded valuesを取得します

AFTimeRange range = new AFTimeRange("*-1d", "*");
AFValues Recorded = pt.RecordedValues(range, OSIsoft.AF.Data.AFBoundaryType.Inside, "", false);
Console.WriteLine("Showing Recorded data from Sinusoid...");
foreach (AFValue val in Recorded)
{
     Console.WriteLine("{0,20}{1,30}", val.Value, val.Timestamp.LocalTime);
}
Console.ReadLine();

   9.1日前から現在まで1時間毎のinterpolated valuesを取得します。

AFTimeSpan span = AFTimeSpan(hour: 1);
AFValues Interpolated = pt.InterpolatedValues(range, span, "", false);
Console.WriteLine("Showing Interpolated data from Sinusoid...");
foreach (AFValue val in Interpolated)
{
 Console.WriteLine("{0,20}{1,30}", val.Value, val.Timestamp.LocalTime);
}
Console.ReadLine();

   10.「s」の文字で始まるすべてのPI Pointを取得します

PIPointQuery query1 = new PIPointQuery(PICommonPointAttributes.Tag, OSIsoft.AF.Search.AFSearchOperator.Equal, "s*");
IEnumerable<PIPoint> foundPoints = PIPoint.FindPIPoints(myPIServer, new PIPointQuery[] { query1 });
foreach (PIPoint tag in foundPoints)
{
     Console.WriteLine(tag.Name);
}
Console.ReadLine();

  11.Sinusoidのタグからdescriptor, location1 , excdevの属性を取得します

IDictionary<string, object> atrs = pt.GetAttributes(new string[] { "descriptor","location1","excdev" });
Console.WriteLine("\n\nSinusoid PI Point attributes: \n");
Console.WriteLine("descriptor = " + atrs["descriptor"]);
Console.WriteLine("location1 = " + atrs["location1"]);
Console.WriteLine("excdev = " + atrs["excdev"]);

12.デバッグを開始し、表示される結果を確認します。

 

なお、複数タグの値を取得する場合、PIPointListを使用するのがよいでしょう。

PIPointList Class

>The PIPointList is the primary way to make bulk data access calls to the PIServer.

 

PIPointListを使用するとBulk callが使用され、パフォーマンスが向上します。

AFSDKにてエレメント、属性にアクセスする方法は以下を参照してください。

AF SDKにてエレメントと属性を作成する方法(基礎)

PI OLEDB Enterpriseでプレースホルダ(?)を使用することがあります。

たとえば以下クエリです。

SELECT ea.Name, s.Value
FROM [OSItest].[Asset].[ElementHierarchy] eh
INNER JOIN [OSItest].[Asset].[ElementAttribute] ea ON ea.ElementID = eh.ElementID
INNER JOIN [OSItest].[Data].[Snapshot] s ON s.ElementAttributeID = ea.ID
WHERE ea.Name = ?

この?には属性名が入ります。

以下はPI SQL Commanderで’湿度'を指定した場合の結果です。

プレースホルダをテストしたくても、PI SQL Commanderでは使えなかったので、C#でテストしました。 (コンソールアプリです)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.OleDb;
using System.Data;

namespace PIOLEDBEnterpriseTest
{
    class Program
    {
        static void Main(string[] args)
        {
            
            String strServer = "khashimotoe6440";
            String connectionstring = "Provider = PIOLEDBENT; Data Source = " + strServer + "; Integrated Security = SSPI;" ;
            String cmdstring = @"
            SELECT ea.Name, s.Value
            FROM [OSItest].[Asset].[ElementHierarchy] eh
            INNER JOIN [OSItest].[Asset].[ElementAttribute] ea ON ea.ElementID = eh.ElementID
            INNER JOIN [OSItest].[Data].[Snapshot] s ON s.ElementAttributeID = ea.ID
            WHERE ea.Name = ?
            ";
            OleDbConnection Cnn = new OleDbConnection(connectionstring);
            Cnn.Open();
            OleDbCommand Cmd = new OleDbCommand(cmdstring, Cnn);
            Cmd.Parameters.AddWithValue("@p1","湿度");
            OleDbDataAdapter da = new OleDbDataAdapter(Cmd);
            DataSet ds = new DataSet();
            da.Fill(ds, "X");
            int num = ds.Tables["X"].Rows.Count;
            for (int i = 0; i < num; ++i)
            {
                Console.WriteLine(ds.Tables["X"].Rows[i].ItemArray[0] + " : " + ds.Tables["X"].Rows[i].ItemArray[1]);
            }           
            Console.Read();
        }
    }
}

 

実行すると、PI SQL Commanderと同じ結果が得られます。

 

さて、ここでea.Name = ?と1つのアイテムなので、問題ないのですが、属性名をinを利用して複数指定する場合、問題が出てきます。

WHERE ea.Name in (?)とし、Cmd.Parameters.AddWithValue("@p1","湿度,温度");としても動作しません。

解決策は以下に書かれていました。

http://stackoverflow.com/questions/1747452/parameters-in-query-with-in-clause

よって、上記リンクのように?,?と複数のプレースホルダを指定するか、in (クエリ)とする方法が考えられます。

?,?と複数のプレースホルダを用意する場合

 WHERE ea.Name in (?,?)
Cmd.Parameters.AddWithValue("@p1","湿度");
Cmd.Parameters.AddWithValue("@p1", "温度");

inの後ろにクエリを書く場合(この場合プレースホルダは置きません)

SELECT ea.Name, s.Value
FROM [OSItest].[Asset].[ElementHierarchy] eh
INNER JOIN [OSItest].[Asset].[ElementAttribute] ea ON ea.ElementID = eh.ElementID
INNER JOIN [OSItest].[Data].[Snapshot] s ON s.ElementAttributeID = ea.ID
WHERE ea.Name in (
    SELECT ea.Name
    FROM [OSItest].[Asset].[ElementHierarchy] eh
    INNER JOIN [OSItest].[Asset].[ElementAttribute] ea ON ea.ElementID = eh.ElementID
)

上記2つとも動作します。

PI UFL Interfaceは多くのPIユーザーにご使用いただいています。

csvファイルのデータを読み込むことができるこのインターフェースは、

csvファイルに適したiniファイルを作成してから動作させる必要があります。

たとえば、csvファイルに

tag名,Value, Timestampの順で、データが入っている場合、それを読み込めるiniファイルを定義する必要があるわけです。

UFLTag1,51.2,2016/09/15 11:13:40

UFLTag1,50.55,2016/09/15 11:14:40

などがcsvの例です。

 

ここではiniファイルの作成方法を簡易的に説明します。

詳細はPI Interface for Universal File and Stream Loading (PI UFL)のユーザーガイドをご参照ください。

 

PI Systemのインターフェースの設定はPI Interface Configuration Utility (PI ICU)で行います。

Windowsボタン→すべてのプログラム→PI SystemPI Interface Configuration Utility (PI ICU)から起動することができ、以下のような画面が表示されます。

既存のサービスがある場合、画面中央右上のドロップダウンボックスから、設定対象のインターフェースを指定します。

または新規作成の場合、フォルダを開くマークをクリックし、PIPC\Interfaces\PI_UFLより、PI_UFL.bat_newを読み込み、新規作成します。

左側のペインにある「UFL」を選択すると、PI UFL Interfaceの設定が上記のように表示されます。

中央の“Configuration File”で指定されているiniファイルが読み込みの定義情報になります。

右側にある"Launch UFLDesigner.exe"のボタンをクリックすると、

指定しているiniファイルを編集するソフトであるUFLDesignerが起動し、そこで設定ファイルの設定等が可能です。

iniファイル自体はUFLDesignerで構築できますし、エディターでの編集も可能です。

 

UFLDesignerでの設定方法を以下に解説します。(詳細はUFLのマニュアルを参照してください。)

まず、csvデータファイルを読み込みます。

File > Open Data Fileよりcsvファイルを開きます。

つぎに1 Generalをクリックします。

1 Generalを定義したら、2 Variableをクリックします。

ここでは変数を定義します。Tag, Timestamp, Valueなどは定義する必要があります。

csvに合わせて変数の数と、Data Typeを定義します。

iniファイル作成後、テキストエディタから変更もできます。

変数の定義ができたら、3 Message Typesをクリックします。

ここではMessage Typesを定義します。MSGは読み込む行により、処理を分けることができます。

ここでは,で始まる行はMSG_1として処理をし、20*で始まる行はMSG_2として処理するという意味になります。

それぞれのMSGで違った処理を行うことができます。

一番典型的なものはcsvファイルにタイトル行が入っている場合です。以下のようなcsvファイルがあった場合、

Tag,Value,Timestamp

UFLTag1,51.2,2016/09/15 11:13:40

UFLTag1,50.55,2016/09/15 11:14:40

...

MSG_1ではC1=="Tag*"とし、

MSG_2ではC1=="*"とすればよいでしょう。

1行目を読んだときはMSG_1として処理されます。

2行目を読んだときはMSG_1は条件として合致しないため、MSG_2に合致するか確認がされ、

MSG_2は"*"となっているため、必ずヒットします。そしてMSG_2でタグに値を書き込む処理を記載すればよいわけです。

ちなみにMSG_1はタイトル行なので、何も処理する必要はありません。

4 Data Extractionをクリックします。

処理を行いたいMSG番号を選択し、+をクリックします。

区切り文字や、文字の位置を定義します。csvであればDelimitedを選択してNextをクリックです。

Commaを選びます。

変数が上部に表示されるので、Data previewにドラッグアンドドロップします。(<Add variable>というところにドロップできます。)

完了したらFinishをクリックします。

 

5 Actionsをクリックします。

ここではCSVファイルのデータを変数に割り当てます。

これはMSGごとに実施します。

プラスボタンをクリックすると、アクションの定義ができます。

StoreInPIPI Data Archiveのタグに値を書き込みます。

右側の白いボックスに変数が出てきますので、Resultの変数部分にドラッグアンドドロップすることで、変数の割当を行えます。

Addボタンをクリックすることで、アクションを追加できます。

iniファイル作成後の手順は以下となります。

  1. UFLの設定ファイルに合わせたデータが入ったCSVファイルを用意します
  2. そのUFL Interfaceが監視しているフォルダにCSVファイルを移動する。
  3. UFL Interfaceを起動する。

 

なお、データに日本語が含まれている場合、文字化けが発生します。

この場合、iniファイル作成後、テキストエディタで文字化けした文字を正しいものに修正します。

 

PI Interfaceの起動・停止はPI ICUからと、マシンのサービス管理から行えます。

PI ICUから行う場合には以下のボタンを使用します。

(この画面では起動ボタンがグレーアウトされており、インターフェースが起動している状態であることを示しています。)

注意点として、過去データをPI Data Archiveに取り込む際にはArchive Fileが用意されている必要があります。

確認はPI System Management Tool (SMT)から行います。

Windowsボタン→すべてのプログラム→PI SystemPI System Management Toolから左下のペインのOperationArchivesを開くとArchive Fileを確認できます。

このスクリーンショットの場合52個のArchive Fileがあり、200811日からのデータを格納できるようになっていますが、それ以前のデータを格納する場合にはアーカイブを作成する必要があります。

一番下にあるアーカイブファイルのStart Time以降のデータであれば格納できることになります。

Archive Fileの作成には左上のボタンをクリックしてウィザードを使って操作を行います。

左上にある白いフォルダのボタン「create a new archive」をクリックすると以下のウインドウが出ます。

上部のMultiple archives for backfillingを選択し、必要事項を入力します。

変更する部分としてはStart TimeおよびEnd Time (過去データの時間範囲にあわせて)

そしてアーカイブファイルを作る頻度になります。上記設定では1週間に1つのアーカイブファイルを作成するという設定ですが、

Weekの部分を変更し1か月に1つという設定でもよいかと思います。

指定入力後にOKを押すとArchive Fileの作成が開始されしばらくすると準備が完了します。

 

PI ICUからPI UFL Interfaceを起動すれば読み込みが開始されます。

うまく読み込まれない場合、iniファイルで指定したOUTPUT=のパスに作成されたファイルを確認し、

問題を調べます。なお、iniファイルでDEB=4など設定すると、より多くのメッセージが出力されます。

 

また、PI UFL Interfaceをインストールすると、Exampleが入ります。

%PIHOME%\Interfaces\PI_UFL\Example

こちらのサンプルもiniファイルの作成の参考にできます。

PI UFL Connectorのiniファイルもほとんど同様の書式となりますが、

こちらにはUFLDesigner.exeと同じようなGUIツールはないため、手動でiniファイルを作成する必要があります。