WinSpec32/WinView32のマクロプログラミング

概略

VBScript,VisualBasicでのプログラム例

Data Acquisition を行うマクロ    データファイル名を要求するダイアログを出し、その名前に変更するマクロ
演算の例    TTL(1番)ポートにパルスを送信しながらスペクトルを取得する ポンプ-プローブの例 

VC++6.0でのマクロ(AddIn)入門(番外編)

概略

WinSpec32/WinView32のマクロ(Automation)は、以下の点で16bit版より優位です。

1. 言語を選ばない
2. 機能(ファンクション)が豊富

1 の特徴は使い慣れた言語(Visual Basic, Visual C++)が使用でき、かつ、その言語のもつ豊富な関数をすべて利用できる事を示しています。例えば、自由にダイアログを設計したり、(既に作成済みの)科学計算用のサブルーチンを使用できます。

2の特徴は、WinSpec32/WinView32のかなりの機能を呼び出す事が出来るようになっており、今後のバージョンアップでも、その範囲は拡大される予定です。

ただし16bitにあったマクロ・レコーディング機能は、現在(Ver. 2.4)サポートされていませんが、Ver. 2.5以降ではマクロレコーディング機能が付加されます。下はα版の図ですが、操作した内容をVBScript言語で記録する事ができるようになります。


VBScriptはWindowsの標準的なスクリプト言語です。これはWindows Scripting Host (WSH)により動作するスクリプト言語です。WSHはWindows98に標準で含まれ、NT4.0やWindows95でもInternet Explorer (4.0 or later) をインストールすれば導入されます。

VBScript言語で書かれたプログラムは、Visual Basicに移行するのが容易なため、はじめVBScriptでテストして、最終的に(実行速度等で有利なため)Visual Basicで動作させると言ったことも可能です。

さらに高度なマクロ(AddIn, SnapIn)を記述するためにVisual C++を使用する事も可能です。例えば、下図は、Visual C++で記述したポンプ&プローブ(SnapIn)プログラムです。WinSpec32で取得したデータを演算し、その結果を独自にグラフ描画しています。

WinSpec32/WinView32にはVisual Basicでのマクロ(SnapIn, AddIn)の作成方法が記述された文書(Winx32 VB Automation 2.4.doc)が付属しています(邦訳は日本ローパーまでお問い合わせください)。本稿では、ダイアログボックス等を必要としない、より簡単なVBScript, Visual Basicでのマクロの例を幾つか示しますが、より詳しい内容に関しては、そちらをご覧下さい。

 

VBScript、Visual Basicによるマクロの例

例1:Data Acquisition を行うマクロ

                       
  Set dx = CreateObject("WinX32.DocFile")                    
  Set Exp1 = CreateObject("WinX32.ExpSetup")                    
  Set Ox = Exp.Start(dx) ' Start Acquisition                    
  Exp1.WaitForExperiment ' Wait for collection to complete

上の3行をエディタ等で作成し、例えばSample.vbsというファイル名で保存します。WinSpec32を起動して、MacroのExecute macro...から実行してみてください。またはVisual Basicをお持ちであれば、Sample.basというファイル名で保存し、Visual Basicでコンパイルの上、実行させて下さい。

WinSpec32は、測定を開始するはずです。

では、マクロの中身を見てみましょう。

WinSpec32/WinView32を総称して"WinX32"と呼びますが、その機能を使用するためにObjectを作成します。1行目では、WinX32の中のExpSetupというObjectを作成しています。そして、ExpSetupの中の一つのファンクションである測定を開始するファンクション-start-と、測定終了を待つファンクション-WaitForExperiment-を使用しています。

このように、WinSpec32/WinView32のマクロとは、Objectを作成して、その中のファンクションを呼ぶという作業を行う事です。では、WinX32には、どのようなObjectがあり、そのObjectは、どんなファンクションを有しているのでしょう? それは、付属のマニュアルをご覧になるか、もしVisual Basicをお使いであればObject Browserを使用する、(Microsoftが配布しているツールである)OLEVIEWを使用するなどの方法があります。

例2:データファイル名を要求するダイアログを出し、その名前に変更するマクロ

                       
  Set Exp1 = CreateObject("WinX32.ExpSetup")                     
  strName=inputbox("ファイル名を入力してください:")                     
  if strName <> "" then                     
      Exp1.SetParam 780, strName  'EXP_DATFILENAME                    
      strMsg=Exp1.GetParam(780)              
  else                   
      strMsg="名前が入力されていません"                 
  end if                     
                    
  wscript.echo strMsg                     

ExpSetup Objectを介してSetParam, GetParamを使用する事で、データファイル名や、露光時間等を取得したり変更したりすることが出来ます。上の780という数字はOLEVIEWで取得しました。

例3: 演算の例

以下の例は、データファイルを開き、その最初のフレームを2倍した値を持つ画像を表示します。なお、VBScriptでは動作しません(後述)。

Dim theFrame As Variant ' Array that holds the image
Dim dx As New WINX32Lib.DocFile
Dim theFrame2 As Variant ' Array that holds the image
Dim dx2 As New WINX32Lib.DocFile
Dim bRes As Integer

' 最初に計算用のデータを開く
dx.Open "D:\temp\sample.spe"
Xdim = dx.GetParam(DM_XDIM) ' get the document x dimension
Ydim = dx.GetParam(DM_YDIM) ' get the document y dimension
' 1 フレーム目を取得
dx.GetFrame 1, theFrame ' retrieve 1 image

' 計算用のイメージを開く
dx2.Open "", Xdim, Ydim, 1, 4, "temp" ' double floating type
' 1 フレーム目を取得
dx2.GetFrame 1, theFrame2 ' retrieve 1 image


'****************************************
' 実際の計算部分
'****************************************

For i = 0 To (Ydim - 1) 'Loop through y dim
    For j = 0 To (Xdim - 1) ' Loop through x dim
        theFrame2(j, i) = theFrame(j, i) * 2
    Next j
Next i

dx2.PutFrame 1, theFrame2
dx2.Update

拡張子をbasとしてセーブし実行します。同様な事はVBScriptでも可能で、以下に例を示します。

Option Explicit 'Strict

Dim dx1

Set dx1 = CreateObject("WinX32.DocFile")

Acquisition(dx1)
CalcSub(dx1)

Sub Acquisition(ByRef dx1)
    Dim dx2, Exp1, xdim, ydim, zdim, strMsg

    Set Exp1 = CreateObject("WinX32.ExpSetup")
    Exp1.Start(dx1)
    Exp1.WaitForExperiment ' Wait for collection to complete


End Sub

Sub CalcSub(ByRef dx1)
    Dim temp
    Dim dx2, Exp1, xdim, ydim, zdim, strMsg, i, j
    Dim datatype

    xdim=dx1.GetParam(6) 'DM_XDIM
    ydim=dx1.GetParam(7) 'DM_YDIM
    zdim=dx1.GetParam(24) 'DM_NUMFRAMES
    datatype=dx1.GetParam(9) 'DM_DATATYPE
    strMsg = CStr(xdim) & "x" & CStr(ydim) & "x" & CStr(zdim) & "のデータを2倍します" 

    Wscript.Echo strMsg

    ' 新しいデータファイルを開く
    ' ディメンジョンはdx1と同じ
    ' ファイル名は"temp"
    Set dx2 = CreateObject("WinX32.DocFile")
    dx2.Open "", xdim, ydim, zdim, datatype, "temp"


    '****************************************
    ' 実際の計算部分
    '****************************************

    For i = 0 To xdim 'Loop through x dim
        For j = 0 To ydim ' Loop through y dim
            temp=2*dx1.GetPixel(1, j, i)
            dx2.PutPixel 1, j, i, temp
        Next
    Next

    dx2.Update

End Sub

ただし、実際に動作させて見ると、非常に遅い事がわかると思います。これは、ピクセルの値を毎回WinSpecに問い合わしているためで、先のVisual Basic版では、フレームごとデータを取得しているため高速に動作するのです。つまり、マクロを高速で動作させるには、WinSpecとの通信オーバーヘッドを減らす事が重要になります。(著者はVisual Basicに精通していないのですが、Visual Basicの例では1フレームのデータを配列で一度に取得している。VBScriptでは、こうした事は出来ない様に思われます。)

例4:TTL(1番)ポートにパルスを送信しながらスペクトルを取得する

PI社のCCDカメラコントローラーには、外部出力用のTTLポートがあります。このポートを使用してステッピングモータを動作させたりする場合のサンプルです。

  Dim Exp1                      
  Dim dx                      
  Dim count                      
  Dim framecount                      
  Dim ttl                   
  count=0                      
                    
  Set dx1 = CreateObject("WinX32.DocFile")                      
  Set Exp1 = CreateObject("WinX32.ExpSetup")                      
 
  Exp1.Start( dx1 )                      
  While Exp1.GetParam(797)    '    797=EXP_RUNNING                    
      framecount=dx.GetParam(91)                     
      If count < framecount Then                      
          ttl=&H00FF                     
          Exp1.SetParam 91, ttl                      
          ttl=&H00FE                  
          Exp1.SetParam 91, ttl                      
          count=count+1                    
      End If                      
  Wend     
  

例5:ポンプ-プローブの例

白色光吸収スペクトルを測定するプログラム(ポンプ-プローブ)を示します。TTLポートの1番に外部シャッターを接続し、そのシャッターでポンプ光を遮断した時のスペクトルと開いた時のスペクトルの除算(透過率)を表示するプログラムです。演算速度の問題からVisual Basicでの記述としております。

    Dim theFrame1 As Variant
    Dim theFrame2 As Variant
    Dim ex As New ExpSetup
    Dim dx1 As DocFile
    Dim dx2 As DocFile
    Dim bRes As Integer
    Dim filename1 As Variant
    Dim filename2 As Variant
    Dim ttl As Variant

    filename1 = "Io"
    filename2 = "I"
    
    ttl = &H0
    
    ex.SetParam EXP_TTL_LINES, ttl
    
    ex.SetParam EXP_DATFILENAME, filename1
    
     If ex.Start(dx1) Then
        ex.WaitForExperiment
    
        dx1.GetFrame 1, theFrame1
    
        ex.SetParam EXP_DATFILENAME, filename2
        ttl = &H1
        ex.SetParam EXP_TTL_LINES, ttl
    
        If ex.Start(dx2) Then
            ex.WaitForExperiment
            dx2.GetFrame 1, theFrame2
            '以上でデータの取得は終了
            For i = LBound(theFrame1, 2) To UBound(theFrame1, 2)
                For j = LBound(theFrame1, 1) To UBound(theFrame1, 1)
                    If theFrame1(j, i) > 0 Then
                        theFrame2(j, i) = theFrame2(j, i) / theFrame1(j, i)
                    Else
                        theFrame2(j, i) = 0
                    End If
                Next
            Next
            dx2.PutFrame 1, theFrame2
            dx2.Update
            
        End If
    End If
 

また、更にTTLポートの3番を使用してパルスを送ってステージを動作させる事を想定したサンプルプログラムも用意しています。こちらは、下のVC++での例と同様、TTLポートの1番と2番に外部シャッターを接続し、1番のシャッターでプローブ光(白色光)を、2番のシャッターでポンプ光をコントロールし、ダークも自動的に測定します。さらに、CCD素子のビニング(グルーピング)がしてあるとして(もしくはDual PDAを使用して)、上の領域に試料を通過するスペクトルを、下の領域でサンプルを通過しない参照スペクトルを取得して、白色光の揺らぎを抑える事が出来るように作成してあります。

その他、WinSpec32/WinView32に付属の文書(Winx32 VB Automation 2.4.doc)には、幾つかのプログラミング例が掲載されています。ぜひ、そちらをご覧になることをお勧めします。

VC++6.0でのマクロ(AddIn)入門

日本ローパー及び、ローパーサイエンティフィック社では、C++言語でのマクロ(Automation)の公式なサポートは行っていません。C++で制御したい方は市販のCOMプログラミングに関する書籍をご覧下さい。ここでは、簡単な例だけを示しておきます。(C++でのプログラミングに関するご質問には回答できません)

下のプログラム例をコンパイルするために、VC++6.0のプロジェクトウィザードからWin32 Console Applicationを作成してください。C++言語では、COMインターフェースの情報をタイプライブラリと呼ばれるファイルから取得します。このために、

#import "C:\Program Files\Princeton Instruments\WinSpec32\WinSpec.exe"

のようにインポートディレクティブを使用します。例として白色光吸収スペクトルを測定するプログラム(ポンプ-プローブ)を示します。Visual Basicでの例より少し凝っています。TTLポートの1番と2番に外部シャッターを接続し、1番のシャッターでプローブ光(白色光)を、2番のシャッターでポンプ光をコントロールし、ダークも自動的に測定します。さらに、CCD素子のビニング(グルーピング)がしてあるとして(もしくはDual PDAを使用して)、上の領域に試料を通過するスペクトルを、下の領域でサンプルを通過しない参照スペクトルを取得して、白色光の揺らぎを抑える事が出来るように作成してあります。2番のシャッターでポンプ光を遮断した時のスペクトルと開いた時のスペクトルの除算(透過率)を表示するプログラムです(ちと長いです)。

#include "stdafx.h"
#include "stdio.h"

#import "C:\Program Files\Princeton Instruments\WinSpec32\WinSpec.exe"

WINX32Lib::IDocFilePtr pMeasureSub(_variant_t Filename, int nCount)
{
    WINX32Lib::IExpSetupPtr pExp( __uuidof(WINX32Lib::ExpSetup) );
    static WINX32Lib::IDocFilePtr pDoc( __uuidof( WINX32Lib::DocFile ));
    long framecount=0;
    long count;     
    short running;
    _variant_t  var;
    short response;

    //  Backgroundを使わない
    var=(long)FALSE;
    pExp->SetParam( WINX32Lib::EXP_BBACKSUBTRACT, &var );

    //  data-typeをLongに固定
    var=(long)WINX32Lib::X_LONG;
    pExp->SetParam( WINX32Lib::EXP_DATATYPE, &var );


    //  Overwriteでいく
    var=(long)WINX32Lib::EXPFA_OVERWRITE;
    pExp->SetParam( WINX32Lib::EXP_FILEACCESS, &var );

    /*  overwrite confirmを消す */
    var=(long)FALSE;
    pExp->SetParam( WINX32Lib::EXP_OVERWRITECONFIRM, &var );

    //  Auto-saveを設定
    var=(long)WINX32Lib::EXPAS_AUTO;
    pExp->SetParam( WINX32Lib::EXP_AUTOSAVE, &var );

    //  ファイル名を設定する
    var=Filename;
    pExp->SetParam( WINX32Lib::EXP_DATFILENAME, &var );

    //  Frame数を設定
    var=(long)nCount;
    pExp->SetParam( WINX32Lib::EXP_SEQUENTS, &var );

    //  測定スタート
    pExp->Start(&pDoc);

    running = TRUE;
    count=1;
    framecount=0;

    /* loop until all frames acquired */
    do
    {
        while (( framecount < count ) && (running))
        {
            /* While Experiment is running */
            var = pDoc->GetParam( WINX32Lib::DM_LASTFRAMERDY, &response );
            framecount = (long) var;                
            running = pExp->GetParam(WINX32Lib::EXP_RUNNING, &response);
        }
        if ( running )
            count++;
    } while (( count <= nCount ) && (running));

    var = pDoc->GetParam( WINX32Lib::DM_LASTFRAMERDY, &response );
    framecount = (long) var;

    /*  ABORT */
    if ( framecount < nCount )
    {
        return  NULL;
    } /* END ABORT */


    return pDoc;

}

int main(int argc, char* argv[])
{
    CoInitialize( NULL );   //  必須

    WINX32Lib::IExpSetupPtr pExp( __uuidof(WINX32Lib::ExpSetup) );
    WINX32Lib::IDocFilePtr pDoc( __uuidof( WINX32Lib::DocFile ));
    WINX32Lib::IDocFilePtr pDocNew( __uuidof( WINX32Lib::DocFile ));
    _variant_t vData, vData2;  // make an empty variant for the data
    SAFEARRAY *pSA, *pSA2;
    LONG    *pDataLong;         //  2
    double  *pDataDouble;       //  4
    long frames;
    short response;
    _variant_t  var;
    int j, k;
    int status = 1;
    _variant_t ttl((long)0x00);
    double *dBkg1, *dBkg2, *dRef, *dTrans;
    double dTmp1, dTmp2;
    int *nAvr;
    variant_t Filename, FilenameOrg;
    long xdim, ydim, zdim;

    xdim = (long) pExp->GetParam( WINX32Lib::EXP_XDIM, &response );
    ydim = (long) pExp->GetParam( WINX32Lib::EXP_YDIM, &response );

    if (ydim!=2){
        //  ROIが2つ設定されていないと動作しない
        CoUninitialize();       //  必須
        return -1;
    }

    dBkg1=new double[xdim];
    dBkg2=new double[xdim];
    dRef=new double[xdim];
    dTrans=new double[xdim];
    nAvr=new int[xdim];

    /* Get the number of frames that we are getting */
    frames=(long)pExp->GetParam( WINX32Lib::EXP_SEQUENTS, &response );

    // new doc xdim X ydim X zdim as float type
    var=pExp->GetParam( WINX32Lib::EXP_DATFILENAME, &response );
    FilenameOrg = var.bstrVal;

    //  まずバックグラウンドを取得
    //  シャッターをクローズ
    ttl = (long)0x0c;
    pExp->SetParam( WINX32Lib::EXP_TTL_LINES, &ttl );
    Filename="PumpPrb_Bkg";
    pDoc=pMeasureSub(Filename, 1);
    if (pDoc==NULL){
        return 0;
    }
    zdim = (long) pDoc->GetParam( WINX32Lib::DM_FRAMECOUNT, &response );

    for (k = 0; k < (long)xdim; k++){
        dBkg1[k]=0.0;
        dBkg2[k]=0.0;
    }

    for ( j=0; j < zdim;j++){
        pDoc->GetFrame(j+1, &vData); // get the data
        pSA =   V_ARRAY(&vData);
        if (S_OK ==(long)SafeArrayAccessData(pSA, (void **)&pDataLong)){
            for (k = 0; k < (long)xdim; k++){
                dBkg1[k]+=(double)pDataLong[k];
                dBkg2[k]+=(double)pDataLong[k+xdim];
            }
            SafeArrayUnaccessData(pSA); // unlock the array
        }
    }
    for (k = 0; k < (long)xdim; k++){
        dBkg1[k]/=(double)zdim;
        dBkg2[k]/=(double)zdim;
    }

    pDoc->Close();

    /*  透過率用のデータを開く  */
    pDocNew->Open("", xdim, 1L, 1L, (long)WINX32Lib::X_DOUBLE, 
        "Trans");  

    WINX32Lib::IDocWindowPtr pWin=pDocNew->GetWindow();

    var=(long)FALSE;
    pWin->SetParam(WINX32Lib::DI_BINITAUTOSCALE, &var);
    var=(double)0.0;
    pWin->SetParam(WINX32Lib::DI_DATAINTENLO, &var);
    var=(double)1.2;
    pWin->SetParam(WINX32Lib::DI_DATAINTENHI, &var);


    //  次にIoを取得
    //  LSBをHighにしてシャッターをオープン
    ttl = (long)0x0d;
    pExp->SetParam( WINX32Lib::EXP_TTL_LINES, &ttl );

    for (int nCount=0;nCount<2; nCount++){
        Filename="PumpPrb_Io";
        pDoc=pMeasureSub(Filename, 1);
        if (pDoc==NULL){
            break;
        }
        zdim = (long) pDoc->GetParam( WINX32Lib::DM_FRAMECOUNT, &response );

        for (k = 0; k < (long)xdim; k++){
            dTrans[k]=0.0;
            nAvr[k]=0;
        }

        for ( j=0; j < zdim;j++){
            pDoc->GetFrame(j+1, &vData); // get the data
            pSA =   V_ARRAY(&vData);
            if (S_OK ==(long)SafeArrayAccessData(pSA, (void **)&pDataLong)){
                for (k = 0; k < (long)xdim; k++){
                    dTmp1=(double)pDataLong[k]-dBkg1[k];
                    dTmp2=(double)pDataLong[k+xdim]-dBkg2[k];
                    if (dTmp2>0.0){
                        dTrans[k]+=dTmp1/dTmp2;
                        nAvr[k]++;
                    }
                }
                SafeArrayUnaccessData(pSA); // unlock the array
            }
        }
        for (k = 0; k < (long)xdim; k++){
            if (nAvr[k]>0)   dTrans[k]/=(double)nAvr[k];
            else            dTrans[k]=0.0;
        }

        pDoc->Close();

        if (nCount==1){
            for (k = 0; k < (long)xdim; k++){
                if (dRef[k]>0.0) dTrans[k]/=dRef[k];
                else            dTrans[k]=0.0;
            }

            for (k = 0; k < (long)xdim; k++)
                dRef[k]=dTrans[k];


            pDocNew->GetFrame(1, &vData2); // get the data
            pSA2 =  V_ARRAY(&vData2);

            if (S_OK == (long)SafeArrayAccessData(pSA2, (void **)&pDataDouble)){
                for (k = 0; k < (long)xdim; k++){
                    pDataDouble[k]=dTrans[k];
                }
                SafeArrayUnaccessData(pSA2); // unlock the array
                pDocNew->PutFrame(1, &vData2);  // stuff the data back into the doc
                pDocNew->Update();
            }
        }

    }

    delete[] dBkg1;
    delete[] dBkg2;
    delete[] dRef;
    delete[] dTrans;
    delete[] nAvr;

    //  シャッターをクローズ
    ttl = (long)0x0c;
    pExp->SetParam( WINX32Lib::EXP_TTL_LINES, &ttl );


    //  ファイル名を戻す
    var=FilenameOrg;
    pExp->SetParam( WINX32Lib::EXP_DATFILENAME, &var );

    //  データタイプを戻す
    var=(long)WINX32Lib::X_UNKNOWN;
    pExp->SetParam( WINX32Lib::EXP_DATATYPE, &var );

    CoUninitialize();       //  必須

    return 0;
}

最終更新日 2000/07/14