SQL expertモードによるPLC実装#

SQL Expertモードとは、PLC上にSQL文を直接記述し、TF6420を通じてデータベースにデータを読み書きする方式です。対してPLC Expertモードと呼ばれるPLCのデータ型を基にデータベースへのデータ読み書きを実現する方法があり、高速に大量のデータを読み書きする場合にはこちらを選択する方が適しています。

しかしSQL固有の機能である、ストアドプロシージャを実行したり、トランザクションにより複数のテーブル間を整合性を保ったまま読み書きしたい場合は、このSQL expertモードを用いることが求められます。

PLC上でSQL文を記述するためのTips#

文字列型の扱い#

STRINGは単体で宣言した場合のサイズは80Byteです。最大値である255Byteを指定する必要があります。

VAR
    sSampleString : STRING(255);
END_VAR

UNICODEを使う場合は、WSTRING型を使います。

VAR
    sSampleString : WSTRING(255);
END_VAR

TwinCATにおける文字列リテラルの定義方法#

文字列結合

SQL文の中にTwinCAT上の変数を埋め込むには、複数の文字列を結合させる必要があります。このとき、CONCAT ファンクションを用います。

エスケープ

SQL文の中で文字列を示すには、TwinCAT同様シングルクオートで囲う必要があります。SQL文中のシングルクオートとTwinCATの文字列範囲を示すシングルクオートを区別するためには、$'のように、SQL文中のシングルクオートの前に$を前置きする必要があります。

VAR
    query_text      : STRING(255);
    sSensorName     : STRING;
    iMeasuredValue  : UINT;
END_VAR

query_text := 'INSERT INTO test_table(name, value) VALUES ($'';
query_text := Tc2_Standard.CONCAT(query_text, '$'');
query_text := Tc2_Standard.CONCAT(query_text, sSensorName);
query_text := Tc2_Standard.CONCAT(query_text, '$', $'');
query_text := Tc2_Standard.CONCAT(query_text, UINT_TO_STRING(iMeasuredValue));
query_text := Tc2_Standard.CONCAT(query_text, '$'');
query_text := Tc2_Standard.CONCAT(query_text, ');'); 

時刻型への変換#

イベントが発生した時刻などを記録する場合、TwinCAT上のコンピュータの時刻データを、SQLデータベース上のDATETIME型のカラムへ記録するする事が求められます。

DATETIME型にはタイムゾーンの情報は載りません。世界中で稼働しているエッジIPCのローカルタイムをそのままデータベースに記録すると、様々な地域のタイムゾーンの時刻が混在してしまう事になります。

そこで、どの地域のエッジ側のIPCであろうと、データベースへ記録する際にはUTC(世界標準時)へ変換して記録し、データベースからデータを取り出して活用する際に任意のタイムゾーンへ変換する運用が望ましいです。TwinCATでこれを実現するためのファンクションブロックを次に挙げます。

FB_LocalSystemTime

現在のコンピュータのロケールのタイムゾーンに調整された、Windows(又はBSD)側の時刻をTIMESTRUCTとして取り出すことができます。

FB_GetTimeZoneInformation

現在のコンピュータのロケールのタイムゾーン情報をST_TimeZoneInformation型で取得できます。

FB_TzSpecificLocalTimeToSystemTime

ST_TimeZoneInformation型によるタイムゾーン情報と、TIMESTRUCTによる時刻情報を入力すると、UTC(世界標準時)のTIMESTRUCTデータが取得できます。

FB_FormatString

C言語のsprintf関数と同じ書式のフォーマットにより、変数を埋め込んだ文字列を生成できます。変数はさまざまな型のデータを入力できるように、T_Arg型で指定します。

以上を用いたUTCへ変換されたDATETIME型の文字列を取得するプログラムを次に示します。

VAR
    fbLocalTime: FB_LocalSystemTime := (bEnable:=TRUE, dwCycle:=1);     // For getting current time as local time
    fbGetTimeZoneInformation: FB_GetTimeZoneInformation := (bExecute := TRUE);      // For getting local timezone information
    fbTzSpecificLocalTimeToSystemTime: FB_TzSpecificLocalTimeToSystemTime;          // FOr converting local time to UTC.    
    sPrintf: Tc2_Utilities.FB_FormatString;
    _text: TEXT; // datetime type strings for SQL databse.
END_VAR

// for 'datetime' value
// Get local time as timestruct type > convert timezone to UTC > output formatted string as "datetime"
fbLocalTime();
fbGetTimeZoneInformation();
fbTzSpecificLocalTimeToSystemTime(in := fbLocalTime.systemTime, tzInfo := fbGetTimeZoneInformation.tzInfo);
sPrintf(sFormat := '%.4d-%.2d-%.2d %.2d:%.2d:%.2d.%.3d',
        arg1 := F_WORD(fbTzSpecificLocalTimeToSystemTime.out.wYear),
        arg2 := F_WORD(fbTzSpecificLocalTimeToSystemTime.out.wMonth),
        arg3 := F_WORD(fbTzSpecificLocalTimeToSystemTime.out.wDay),
        arg4 := F_WORD(fbTzSpecificLocalTimeToSystemTime.out.wHour),
        arg5 := F_WORD(fbTzSpecificLocalTimeToSystemTime.out.wMinute),
        arg6 := F_WORD(fbTzSpecificLocalTimeToSystemTime.out.wSecond),
        arg7 := F_WORD(fbTzSpecificLocalTimeToSystemTime.out.wMilliseconds));
_text := sPrintf.sOut;