Euresys::EGrabber

略して「eGrabber」とも呼ばれる Euresys::EGrabber は、高レベルのインターフェースを提供するC++クラスのライブラリです。Euresys::EGenTLライブラリを基礎に構築されており、ほとんどのユーザーにお勧めです。

Euresys::EGrabberC++クラス上に構築された.NETアセンブリも提供されていますが、このドキュメントでは、主にC++ APIを中心に説明します。C++インターフェースと.NETインターフェースの小さな違いは、これについて特別に用意された章に一覧表示されています。

eGrabberにはPythonバインディングも用意されています。C++インターフェースとPythonインターフェースの違いについても、それに焦点を当てた章に一覧表示されています。

ここで説明するクラスを使用するには、メインのEuresys::EGrabberファイルを含める必要があります。

                
                    #include <EGrabber.h>
                
            

Euresys::EGrabberはヘッダーのみのライブラリです(libまたはdllファイルとして提供されていません1)。複数のクラスで構成されており、そのうち最も重要なクラスもEuresys::EGrabberと名付けられています。

                
                    namespace Euresys {
    class EGrabber;
}
            

このテキストでは、このクラスをグラバーと呼んでおり、関連する一連のGenTLモジュールはグラバーによってカプセル化されます。

インターフェース: グローバル(共通)フレームグラバーの設定と機能を表すモジュールで、デジタルI/O制御、PCIe、およびファームウェアステータスなどが含まれます。
デバイス(またはリモートデバイスに対するローカルデバイス): フレームグラバーの設定とカメラに関連する機能を含むモジュールで、ストロボ、トリガーなど、主にカメラと照明制御機能から構成されます。
データストリーム: 画像バッファーを処理するモジュール
リモートデバイス: CoaXPressカメラ
バッファー数

上記の概念の詳しい説明については、GenTLモジュールに関する章をご覧ください。

最初の例

この例では、グラバーを作成し、それに含まれるインターフェース、デバイス、およびリモートデバイスモジュールに関する基本情報を表示します。

                    
                        #include <iostream>  
#include <EGrabber.h>                                                                   // 1  

static  const  uint32_t CARD_IX = 0;
static  const  uint32_t DEVICE_IX = 0;

void showInfo() {
    Euresys::EGenTL gentl;                                                              // 2
    Euresys::EGrabber<> grabber(gentl, CARD_IX, DEVICE_IX);                             // 3

    std::string card = grabber.getString<Euresys::InterfaceModule>("InterfaceID");      // 4
    std::string dev = grabber.getString<Euresys::DeviceModule>("DeviceID");             // 5  
    int64_t width = grabber.getInteger<Euresys::RemoteModule>("Width");                 // 6  
    int64_t height = grabber.getInteger<Euresys::RemoteModule>("Height");               // 6

    std::cout << "Interface:    " << card << std::endl;
    std::cout << "Device:       " << dev  << std::endl;
    std::cout << "Resolution:   " << width << "x" << height << std::endl;
}

int main() {
    try {                                                                               // 7
        showInfo();
    } catch (const std::exception &e) {                                                 // 7
        std::cout << "error: " << e.what() << std::endl;
    }
}
                
1. EGrabber.hを含めます。これはEuresys::EGrabberクラスを定義するもので、必要となるほかのヘッダーファイル(EGenTL.hや標準のGenTLヘッダーファイルなど)も含まれます。
2. Euresys::EGenTLオブジェクトを作成します。これには以下のような操作が含まれます。
Coaxlink GenTLプロデューサー(coaxlink.cti)を見つけて動的に読み込みます。
coaxlink.ctiがエクスポートした関数へのポインターを取得し、Euresys::EGenTLメソッドを介して利用可能にします。
coaxlink.ctiを初期化します(GenTLの初期化関数GCInitLibを呼び出します)。
3. Euresys::EGrabberオブジェクトを作成します。コンストラクターには、手順2で作成されたgentlオブジェクトが必要です。また、オプションの引数として、使用するインターフェースとデバイスのインデックスを取ります。
EGrabberの後にある山括弧(<>)については、後で説明しますので、ここでは無視しても構いません。
4. GenApiを使用し、CoaxlinkカードのIDを調べます。Euresys::InterfaceModuleは、 インターフェースモジュールからの返答をが必要であることを示しています。
5. 同様に、デバイスのIDを調べます。今回は、Euresys::DeviceModuleを使用してデバイスモジュールをターゲットにします。
6. 最後に、カメラの解像度を読み取ります。Euresys::RemoteModuleは、値をカメラから取得する必要があることを示します。
7. Euresys::EGrabberは例外を使用してエラーをレポートするため、コードをtry ... catchブロック内に包括します。

プログラムの出力例:

                Interface:    PC1633 - Coaxlink Quad G3 (2-camera) - KQG00014
 Device:       Device0
 Resolution:   4096x4096
            

画像の取得

このプログラムは、Euresys::EGrabberを使用して、Coaxlinkカードに接続されたカメラから画像を取得します。

                    
                        #include <iostream>  
#include <EGrabber.h>  

void grab() {
    Euresys::EGenTL gentl;
    Euresys::EGrabber<> grabber(gentl);                                     // 1

    grabber.reallocBuffers(3);                                              // 2
    grabber.start(10);                                                      // 3  
    for (size_t i = 0; i < 10; ++i) {
        Euresys::ScopedBuffer buf(grabber);                                 // 4  
        void *ptr = buf.getInfo<void *>(GenTL::BUFFER_INFO_BASE);           // 5  
        uint64_t ts = buf.getInfo<uint64_t>(GenTL::BUFFER_INFO_TIMESTAMP);  // 6
        std::cout << "buffer address: " << ptr << ", timestamp: " 
                  << ts << " us" << std::endl;
    }                                                                       // 7
}

int main() {
    try {
        grab();
    } catch (const std::exception &e) {
        std::cout << "error: " << e.what() << std::endl;
    }
}
                
1. Euresys::EGrabberオブジェクトを作成します。コンストラクターの2番目と3番目の引数はここでは省略されていますが、グラバーは、システムに存在する最初のインターフェースの最初のデバイスを使用します。
2. 3つのバッファーを割り当てます。必要なバッファーサイズは、グラバーが自動的に判断します。
3. 取り込みを開始します。ここでは、グラバーに10個のバッファーを埋めるように指示します。特定のバッファー数に達した後にグラバーを停止しない場合は、grabber.start(GenTL::GENTL_INFINITE)、または単に grabber.start()を実行できます。
取り込みを開始すると、次の操作が実行されます。
カメラでAcquisitionStartコマンドが実行されます。
データストリームを開始するために、DSStartAcquisition関数が呼び出されます。

この例では、カメラとフレームグラバーが正しく設定されていることを前提としています。実際のアプリケーションでは、取得を開始する前(およびそれに関してバッファーを割り当てる前)に設定スクリプトを実行するとより安全といえます。これは別の例でも示されています。

4. バッファーがグラバーでいっぱいになるまで待ちます。結果はEuresys::ScopedBufferです。このscopedは、バッファーの存続期間が現在の範囲(現在のブロック)であることを示すために使用されます。
5. バッファーアドレスを取得します。バッファーのgetInfoメソッドを呼び出して行われます。このメソッドは引数としてBUFFER_INFO_CMDを取ります。この場合は、標準のGenTLヘッダーファイルで定義されているBUFFER_INFO_BASEを要求しています。
                    
                        enum BUFFER_INFO_CMD_LIST
{
  BUFFER_INFO_BASE      = 0, /* PTR    Base address of the buffer memory. */
  BUFFER_INFO_SIZE      = 1, /* SIZET  Size of the buffer in bytes. */
  BUFFER_INFO_USER_PTR  = 2, /* PTR    Private data pointer of the GenTL Consumer. */
  BUFFER_INFO_TIMESTAMP = 3, /* UINT64 Timestamp the buffer was acquired. */  
  // ...  
  // other BUFFER_INFO definitions omitted  
  // ...
  BUFFER_INFO_CUSTOM_ID = 1000  /* Starting value for GenTL Producer custom IDs. */
};
typedef  int32_t BUFFER_INFO_CMD;
                

getInfoはテンプレートメソッドであり、呼び出すときは、期待する値の型を指定する必要があります。BUFFER_INFO_BASEがポインターを返します。これが、getInfo<void *>を使用する理由です。

6. 同じ手順を繰り返して、バッファーのタイムスタンプを取得します。今回は、BUFFER_INFO_TIMESTAMPの型に一致するように、uint64_tバージョンのgetInfoを使用します。

タイムスタンプは常に64ビットで、コンピューターが起動してから経過したマイクロ秒数として表されます。

7. forブロックの終わりに達しました。ローカル変数bufが範囲外となり破棄されます。ここでScopedBufferデストラクターが呼び出されています。これにより、bufに含まれているGenTLバッファーがグラバーのデータストリームのキューに戻されます。

プログラムの出力例:

                buffer address: 0x7f3c32c54010, timestamp: 11247531003686 us
 buffer address: 0x7f3c2c4bf010, timestamp: 11247531058080 us
 buffer address: 0x7f3c2c37e010, timestamp: 11247531085003 us
 buffer address: 0x7f3c32c54010, timestamp: 11247531111944 us
 buffer address: 0x7f3c2c4bf010, timestamp: 11247531137956 us
 buffer address: 0x7f3c2c37e010, timestamp: 11247531163306 us
 buffer address: 0x7f3c32c54010, timestamp: 11247531188600 us
 buffer address: 0x7f3c2c4bf010, timestamp: 11247531213807 us
 buffer address: 0x7f3c2c37e010, timestamp: 11247531239158 us
 buffer address: 0x7f3c32c54010, timestamp: 11247531265053 us
            

割り当てられた3つのバッファーについて、0x7f3c32c54010をA、0x7f3c2c4bf010をB、0x7f3c2c37e010をCと呼ぶと、A→B→C→A→B→C→...のようにラウンドロビン方式で使用されていることがわかります。これは次のことに起因しています。

入力および出力バッファーキューのFIFOの特性:
Coaxlinkドライバーは入力キューの先頭からバッファーをポップし、DMA転送を行うために、Coaxlinkカードに渡します。
転送が完了すると、バッファーは出力キューの最後にプッシュされます。
ScopedBufferの使用:
ScopedBufferコンストラクターは出力キューの先頭からバッファーをポップします(つまり、最も古いバッファーが取得されます)。
ScopedBufferデストラクターはそのバッファーを入力キューの最後にプッシュします(したがって、このバッファーはすでに入力キューにあるすべてのバッファーの後に新しい転送に使用されます)。

グラバーの設定

設定は、すべての画像の取り込みプログラムにおいて非常に重要な要素です。

カメラとフレームグラバーはいずれもアプリケーションの要件に従って設定されている必要があります。
カメラの設定はフレームグラバーの設定と互換性がある必要があります。

設定は基本的に、グラバーモジュールのリモートデバイス(カメラ)、インターフェースデバイス、またはデータストリームモジュールで実行される一連の set/get機能に集約されます。

このプログラムはグラバーをいわゆるRG制御モード(非同期リセットカメラ制御、フレームグラバー制御露出)向けに設定します。

                    
                        #include <iostream>  
#include <EGrabber.h>  

const  double FPS = 150;

void configure() {
    Euresys::EGenTL gentl;
    Euresys::EGrabber<> grabber(gentl);
    // camera configuration
    grabber.setString<Euresys::RemoteModule>("TriggerMode", "On");                      // 1
    grabber.setString<Euresys::RemoteModule>("TriggerSource", "CXPin");                 // 2
    grabber.setString<Euresys::RemoteModule>("ExposureMode", "TriggerWidth");           // 3  
    // frame grabber configuration
    grabber.setString<Euresys::DeviceModule>("CameraControlMethod", "RG");              // 4
    grabber.setString<Euresys::DeviceModule>("CycleTriggerSource", "Immediate");        // 5
    grabber.setFloat<Euresys::DeviceModule>("CycleTargetPeriod", 1e6 / FPS);            // 6
}

int main() {
    try {
        configure();
    } catch (const std::exception &e) {
        std::cout << "error: " << e.what() << std::endl;
    }
}
                
1. カメラのトリガーを有効にします。
2. CoaXPressリンクのトリガーを受信するようにカメラを設定します。
3. TriggerWidth露出モードを使用するようにカメラを設定します。
4. フレームグラバーのカメラ制御をRGに設定します。このモードでは、カメラサイクルの開始と露出時間の制御はフレームグラバーによって行われます。
5. ハードウェアやソフトウェアのトリガーを待たずに、フレームグラバーにカメラサイクルを開始するように設定します(CycleTargetPeriodで定義されたレートで)。
6. フレームレートを設定します。

ただし、より優れた方法でグラバーを設定することができます。スクリプトファイルを使用すると、プログラムは次のようになります。

                    
                        #include <iostream>  
#include <EGrabber.h>  

void configure() {
    Euresys::EGenTL gentl;
    Euresys::EGrabber<> grabber(gentl);
    grabber.runScript("config.js");
}

int main() {
    try {
        configure();
    } catch (const std::exception &e) {
        std::cout << "error: " << e.what() << std::endl;
    }
}
                

また、設定スクリプトは次のようになります。

                    
                        var grabber = grabbers[0];  
var FPS =  150;  
// camera configuration  
grabber.RemotePort.set("TriggerMode",  "On");  
grabber.RemotePort.set("TriggerSource",  "CXPin");  
grabber.RemotePort.set("ExposureMode",  "TriggerWidth");  
// frame grabber configuration  
grabber.DevicePort.set("CameraControlMethod",  "RG");  
grabber.DevicePort.set("CycleTriggerSource",  "Immediate");  
grabber.DevicePort.set("CycleTargetPeriod",  1e6 / FPS);
                

スクリプトファイルの使用には、次のようないくつかのメリットがあります。

アプリケーションを再コンパイルせずに設定を変更できるため、現像サイクルを短縮できるだけでなく、ラボや現場での設定更新が可能です。
設定スクリプトをGenICamブラウザとコマンドラインのgentlツールを使って読み込むことができます。ユーザーアプリケーションの外部で設定の検証が可能です。
設定スクリプトは、C++、C#、VB.NETなどの異なるプログラミング言語で書かれた複数のアプリケーション間で簡単に共有できます。
Euresys GenApiスクリプトの全性能を利用できます。

イベント

背景

Coaxlinkカードが生成するイベントには、次のさまざまな種類があります。

新規バッファーイベント: バッファーデータストリームによっていっぱいになったことを示すイベント。
データストリームイベント: データストリームやそのフレームの保管に関連するイベント。
カメラとイルミネーションコントローラーイベント: カメラとそのイルミネーションデバイスのリアルタイム制御(デバイスが実行)に関連するイベント。
I/Oツールボックスイベント: デジタルI/Oラインやその他のI/Oツールに関連するイベント(インターフェースから送信される)。
CoaXPressインターフェースイベント: CoaXPressインターフェースに関連するイベント(インターフェースから送信される)。

新規バッファーはGenTLの標準的なイベントです。バッファーがフレームグラバーでいっぱいになったときに発生します。新規バッファーイベントに伴う情報には、バッファーのハンドルとタイムスタンプが含まれます。

その他のイベントはCoaxlinkに限定されており、特定のイベントのカテゴリとして表示できます。たとえば、イベントのCICカテゴリには次の項目があります。

CameraTriggerRisingEdge(カメラトリガー開始)
CameraTriggerFallingEdge(カメラトリガー終了)
StrobeRisingEdge(ストロボ開始)
StrobeFallingEdge(ストロボ終了)
AllowNextCycle(次のカメラサイクルに対するCICの準備完了)
...

また、イベントのI/Oツールボックスカテゴリには次の項目があります。

LIN1(行入力ツール1)
LIN2(行入力ツール2)
MDV1(マルチプライヤ/ディバイダツール1)
...

カウンター

Coaxlinkファームウェアは、各イベントの発生をそれぞれカウントし (新規バッファーイベントを除く)、このカウンターをEventCountという名前のGenApi機能で利用できるようにします。各イベントには専用のカウンターが割り当てられており、選択されたイベントに応じてEventCountの値が変わります。

                        
                            // select the CameraTriggerRisingEdge event
grabber.setString<DeviceModule>("EventSelector", "CameraTriggerRisingEdge");
// read the value of the counter  
int64_t counter = grabber.getInteger<DeviceModule>("EventCount");
                    

選択機能の表記を使用すると、次のようになります。

                        
                            // read the value of the CameraTriggerRisingEdge counter  
int64_t counter = grabber.getInteger<DeviceModule>("EventCount[CameraTriggerRisingEdge]");
                    

通知

上記で説明したように、イベントが発生すると、そのイベント専用のカウンターが増分します。Coaxlinkは、Euresys::EGrabberにユーザー定義のコールバック関数を実行させることによって、このイベントをアプリケーションに通知することもできます。ただし、最初に、1つ以上のイベントの通知を有効にしておく必要があります。

                        grabber.setString<DeviceModule>("EventSelector", "CameraTriggerRisingEdge");
grabber.setInteger<DeviceModule>("EventNotification", true);
grabber.setString<DeviceModule>("EventSelector", "CameraTriggerFallingEdge");
grabber.setInteger<DeviceModule>("EventNotification", true);
...
                    

または、次のように記述します。

                        grabber.setInteger<DeviceModule>("EventNotification[CameraTriggerRisingEdge]", true);
grabber.setInteger<DeviceModule>("EventNotification[CameraTriggerFallingEdge]", true);
...
                    

設定スクリプトを使用すると、すべてのイベントの通知を簡単に有効化することができます。

                        
                            function  enableAllEvents(p) {                             // 1  
    var events =  p.$ee('EventSelector');                  // 2  
    for (var e of events) {  
        p.set('EventNotification['  + e +  ']',  true);      // 3  
    }  
}  

var grabber = grabbers[0];  
enableAllEvents(grabber.InterfacePort);                   // 4  
enableAllEvents(grabber.DevicePort);                      // 5  
enableAllEvents(grabber.StreamPort);                      // 6
                    
1. 引数としてモジュール(またはポート)pを取るenableAllEventsというヘルパー関数を定義します。
2. $ee関数を使用して、EventSelectorが取ることができる値のリストを取得します。これがモジュールpが生成したイベントのリストです(eeenum entry[列挙型エントリ]の略)。
3. イベントごとに通知を有効化します。(+演算子は文字列を連結するため、e'LIN1'である場合、式'EventNotification[' + e + ']''EventNotification[LIN1]'と評価されます。)
4. インターフェースモジュールに対し、手順1で定義したenableAllEvents関数を呼び出します。これにより、I/O ツールボックスCoaXPressインターフェースカテゴリ内のすべてのイベントに対する通知が有効になります。
5. 同様に、デバイスモジュールからのすべてのイベント(CIC イベント)に対しても、通知を有効にします。
6. 最後に、すべてのデータストリームイベントに対して通知を有効にします。

コールバック関数

イベントが発生し、そのイベントに対してイベント通知が有効である場合、Euresys::EGrabberは複数のコールバック関数の1つを実行します。

これらのコールバック関数は、オーバーライドされた仮想メソッドで定義されています。

                        
                            class MyGrabber : public Euresys::EGrabber<>
{
    public:
        ...

    private:
        // callback function for new buffer events  
        virtual  void onNewBufferEvent(const NewBufferData& data) {
            ...
        }

        // callback function for data stream events  
        virtual  void onDataStreamEvent(const DataStreamData &data) {
            ...
        }

        // callback function for CIC events  
        virtual  void onCicEvent(const CicData &data) {
            ...
        }

        // callback function for I/O toolbox events  
        virtual  void onIoToolboxEvent(const IoToolboxData &data) {
            ...
        }

        // callback function for CoaXPress interface events  
        virtual  void onCxpInterfaceEvent(const CxpInterfaceData &data) {
            ...
        }
};
                    

ここに示すとおり、イベントのカテゴリごとに異なるコールバック関数を定義できます。

.NETのコールバック関数は、仮想メソッドをオーバーライドするのではなく、デリゲートの作成によって定義されます。例は、.NETアセンブリに関する章をご覧ください。

イベントID

イベントがアプリケーションに通知される際、実行されるコールバック関数によって、そのイベントのカテゴリが示されます。発生した実際のイベントは、numidと呼ばれる数値IDによって識別され、include/GenTL_v1_5_EuresysCustom.hで定義されています。

                        
                            enum EVENT_DATA_NUMID_CUSTOM_LIST
{
    // EVENT_CUSTOM_IO_TOOLBOX
    EVENT_DATA_NUMID_IO_TOOLBOX_LIN1                    = ...  /* Line Input Tool 1 */
    EVENT_DATA_NUMID_IO_TOOLBOX_LIN2                    = ...  /* Line Input Tool 2 */
    EVENT_DATA_NUMID_IO_TOOLBOX_MDV1                    = ...  /* Multiplier/Divider Tool 1 */
    ...
    // EVENT_CUSTOM_CXP_INTERFACE
    ...
    // EVENT_CUSTOM_CIC
    EVENT_DATA_NUMID_CIC_CAMERA_TRIGGER_RISING_EDGE     = ...  /* Start of camera trigger */
    EVENT_DATA_NUMID_CIC_CAMERA_TRIGGER_FALLING_EDGE    = ...  /* End of camera trigger */
    EVENT_DATA_NUMID_CIC_STROBE_RISING_EDGE             = ...  /* Start of light strobe */
    EVENT_DATA_NUMID_CIC_STROBE_FALLING_EDGE            = ...  /* End of light strobe */
    ...
    // EVENT_CUSTOM_DATASTREAM
    EVENT_DATA_NUMID_DATASTREAM_START_OF_CAMERA_READOUT = ...  /* Start of camera readout */
    EVENT_DATA_NUMID_DATASTREAM_END_OF_CAMERA_READOUT   = ...  /* End of camera readout */
    ...
};
                    

参考までに、次の項目との関係をその下の表に示します。

イベント生成モジュール
イベントのカテゴリ
コールバック関数の名前
コールバック関数に渡されるデータ型
共通のnumid接頭辞
モジュール カテゴリ コールバック関数 データ型 numid接頭辞
データストリーム 新規バッファー onNewBufferEvent NewBufferData -
データストリーム データストリーム onDataStreamEvent DataStreamData EVENT_DATA_NUMID_DATASTREAM_
デバイス CIC onCicEvent CicData EVENT_DATA_NUMID_CIC_
インターフェース I/Oツールボックス onIoToolboxEvent IoToolboxData EVENT_DATA_NUMID_IO_TOOLBOX_
インターフェース CXPインターフェース onCxpInterfaceEvent CxpInterfaceData EVENT_DATA_NUMID_CXP_INTERFACE_
注意
新規バッファーイベントカテゴリにはイベントが1つしかないため、numidは必要ありません。
命名規則は単純です。some categoryという名前のイベントカテゴリを例とした場合、コールバック関数はonSomeCategoryEventで、SomeCategoryData構造を引数として取り、共通のnumid接頭辞としてEVENT_DATA_NUMID_SOME_CATEGORY_を使用します。

イベントとコールバックを説明する完全なサンプルプログラムをいくつか紹介しますが、その前にもう1つ説明しておくべきことがあります。コールバック関数が実行されるコンテキストについてです。これは次のセクションで紹介する内容です。

EGrabberの特色

コールバック関数はいつ呼び出されるのか?どのコンテキスト(どのスレッドから)から呼び出されるのか?これらの質問について考えると、いくつかのコールバックモデルが定義されます。

アプリケーションがグラバーに対して、バッファーやイベントがあるのかを問い合わせます。そして、ある場合はすぐにそのコールバック関数を実行するように指示します。これはポーリング式の同期モードの演算で、アプリケーションスレッドでアプリケーションがコールバックの実行を要求したときに実行されます。
これはオンデマンドと呼ばれるコールバックモデルです。
アプリケーションがグラバーに対して、単一の専用スレッドを作成して、このスレッド内でイベントを待つように要求します。イベントが発生すると、グラバーは対応するコールバック関数をこのコールバックスレッドでも実行します。
これはシングルスレッドと呼ばれるコールバックモデルです。
アプリケーションがグラバーに対して、各コールバック関数に専用のスレッドを作成するように要求します。それぞれのスレッドにて、グラバーは特定のイベントを待ちます。イベントが発生すると、対応するコールバック関数をそのスレッドで実行します。
これはマルチスレッドと呼ばれるコールバックモデルです。

これら3つのコールバックモデルはすべて合理的であり、どれが最適であるかはアプリケーションによって異なります。

オンデマンドモデルは最もシンプルです。実装にはワーカースレッドを使用することもありますが、ユーザーの観点ではスレッドは追加されません。アプリケーション側で、スレッドの同期やミューテックスなどを気にする必要がありません。
ユーザーが専用のコールバックスレッドを必要とする場合、シングルスレッドモデルで解決できます。
スレッドの数が1つであれば複雑なことではありません。このモデルの場合、グラバーを使用しているスレッドが少なくとも2つあるため、同期や共有データを考慮する必要があります。
マルチスレッドモデルでは、イベントの各カテゴリは独自のスレッドを取得します。これには、ある種のイベント(およびコールバック関数の実行)によって、ほかの種類のイベントの通知が遅延することがないというメリットがあります。たとえば、あるスレッドがonNewBufferEventで時間のかかる画像処理を行っている場合でも、onCicEventによるCICイベント(露出が完了し、オブジェクトまたはカメラを移行できることを示すイベントなど)の通知が遅延することはありません。
このモデルでは、グラバーを使用するスレッドが複数あるため、シングルスレッドモデルと同様に同期を行う必要があります。
さらにアプリケーション側は、すでに受信し処理されたYという種類のイベントの通知よりも古いXという種類のイベントの通知を受信する可能性があることに注意しておかなければなりません。結局のところ、この点がシングルスレッドモデルとマルチスレッドモデルを区別する要因といえます。

ユーザーが最大限の柔軟性を得られるように、3つの全コールバックモデルがサポートされています。そこで必要となるのが、Euresys::EGrabberのさまざまな手法です。ここまでは、EGrabber<>の山括弧の説明を後回しにしてきました。EGrabberクラスは、実際には、別の型によってパラメーター化されたテンプレートクラスです。

この場合、テンプレートパラメーターは、使用されるいずれか(CallbackOnDemandCallbackSingleThread、またはCallbackMultiThread)のコールバックモデルです。
空の<>は、デフォルトのテンプレートパラメーター(CallbackOnDemand)を選択するために使用されます。
インスタンス化できるグラバーには次の種類があります。
EGrabber<CallbackOnDemand>
EGrabber<CallbackSingleThread>
EGrabber<CallbackMultiThread>
EGrabber<>EGrabber<CallbackOnDemand>と同等)
EGrabberヘッダーファイルでは、次の型エイリアス(同義語)も定義されています。
                    
                        typedef EGrabber<CallbackOnDemand>     EGrabberCallbackOnDemand;
typedef EGrabber<CallbackSingleThread> EGrabberCallbackSingleThread;
typedef EGrabber<CallbackMultiThread>  EGrabberCallbackMultiThread;
                
.NETジェネリックはC++テンプレートと完全には一致しておらず、.NETにはテンプレートEGrabberクラスが存在しないため、EGrabberCallbackOnDemandEGrabberCallbackSingleThread、またはEGrabberCallbackMultiThreadのいずれかを使用する必要があります。

イベントとコールバックの例

オンデマンドコールバック

このプログラムは、グラバーが生成したCICイベントに関する基本情報を表示します。

                        
                            #include <iostream>  
#include <EGrabber.h>  

using  namespace Euresys;                                                        // 1  

class MyGrabber : public EGrabber<CallbackOnDemand> {                           // 2  
    public:
        MyGrabber(EGenTL &gentl) : EGrabber<CallbackOnDemand>(gentl) {          // 3
            runScript("config.js");                                             // 4
            enableEvent<CicData>();                                             // 5
            reallocBuffers(3);
            start();
        }

    private:
        virtual  void onCicEvent(const CicData &data) {
            std::cout << "timestamp: " << std::dec << data.timestamp << " us, "  // 6
                      << "numid: 0x" << std::hex << data.numid                  // 6
                      << " (" << getEventDescription(data.numid) << ")"
                      << std::endl;
        }
};

int main() {
    try {
        EGenTL gentl;
        MyGrabber grabber(gentl);
        while (true) {
            grabber.processEvent<CicData>(1000);                                // 7
        }
    } catch (const std::exception &e) {
        std::cout << "error: " << e.what() << std::endl;
    }
}
                    
1. このusing命令を使うと、Euresys::Xyzの代わりにXyzと記述することができます。そのため、各行を比較的、短くすることができます。
2. EGrabber<CallbackOnDemand>から派生した新しいクラスMyGrabberを定義します。
3. MyGrabberのコンストラクターがEGrabber<CallbackOnDemand>のコンストラクターを呼び出して、その基本クラスを初期化します。
4. config.jsスクリプトを実行します。このスクリプトでは、次のことが行われます。
カメラとフレームグラバーを適切に設定します。
CICイベントの通知を有効化します。
5. onCicEventコールバックを有効にします。
6. onCicEventコールバック関数がconst CicData &を受け取ります。この構成はinclude/EGrabberTypes.hで定義されています。発生したイベントに関する情報がいくつか含まれています。ここでは、各イベントのtimestampnumidを表示しています。numidはどのCICイベントが発生したかを示します。
7. processEvent<CicData>(1000)を呼び出します。
グラバーがCICイベントを待ち始めます。
1000ミリ秒以内にイベントが発生した場合、グラバーはonCicEventコールバック関数を実行します。
発生しなかった場合は、タイムアウト例外が発生します。

プログラムの出力例:

                    timestamp: 1502091779 us, numid: 0x8041 (Start of camera trigger)
 timestamp: 1502091784 us, numid: 0x8048 (Received acknowledgement for previous CXP trigger message)
 timestamp: 1502091879 us, numid: 0x8043 (Start of light strobe)
 timestamp: 1502092879 us, numid: 0x8044 (End of light strobe)
 timestamp: 1502097279 us, numid: 0x8042 (End of camera trigger)
 timestamp: 1502097284 us, numid: 0x8048 (Received acknowledgement for previous CXP trigger message)
 timestamp: 1502191783 us, numid: 0x8041 (Start of camera trigger)
 timestamp: 1502191783 us, numid: 0x8045 (CIC is ready for next camera cycle)
 timestamp: 1502191788 us, numid: 0x8048 (Received acknowledgement for previous CXP trigger message)
 timestamp: 1502191883 us, numid: 0x8043 (Start of light strobe)
 timestamp: 1502192883 us, numid: 0x8044 (End of light strobe)
 timestamp: 1502197283 us, numid: 0x8042 (End of camera trigger)
 timestamp: 1502197288 us, numid: 0x8048 (Received acknowledgement for previous CXP trigger message)
 timestamp: 1502291788 us, numid: 0x8041 (Start of camera trigger)
 timestamp: 1502291788 us, numid: 0x8045 (CIC is ready for next camera cycle)
 ...
                

シングルスレッドとマルチスレッドのコールバック

このプログラムも、グラバーが生成したCICイベントに関する基本情報を表示しますが、CallbackSingleThreadモデルを使って行います。

                        
                            #include <iostream>  
#include <EGrabber.h>  

using  namespace Euresys;

class MyGrabber : public EGrabber<CallbackSingleThread> {                       // 1  
    public:
        MyGrabber(EGenTL &gentl) : EGrabber<CallbackSingleThread>(gentl) {      // 2
            runScript("config.js");
            enableEvent<CicData>();
            reallocBuffers(3);
            start();
        }

    private:
        virtual  void onCicEvent(const CicData &data) {
            std::cout << "timestamp: " << std::dec << data.timestamp << " us, "
                      << "numid: 0x" << std::hex << data.numid
                      << " (" << getEventDescription(data.numid) << ")"
                      << std::endl;
        }
};

int main() {
    try {
        EGenTL gentl;
        MyGrabber grabber(gentl);
        while (true) {                                                          // 3
        }
    } catch (const std::exception &e) {
        std::cout << "error: " << e.what() << std::endl;
    }
}
                    

このプログラムとCallbackOnDemandバージョンの違いはほとんどありません。

1. MyGrabberは、EGrabber<CallbackOnDemand>ではなくEGrabber<CallbackSingleThread>から派生しています。
2. そのため、MyGrabberのコンストラクターは、EGrabber<CallbackSingleThread>のコンストラクターを呼び出して、その基本クラスを初期化します。
3. processEventを呼び出すコールバックスレッドはEGrabberが作成するため、何も行う必要はありません。ここでは、単に無限ループが開始されます。

このように、CallbackOnDemandからCallbackSingleThreadへの移行は非常に簡単です。また、代わりにCallbackMultiThreadバリアントを使用する場合でも、MyGrabberの基本クラスをEGrabber<CallbackMultiThread>に変更し、後は適切なコンストラクターを呼び出すだけで実現できます。

新規バッファーのコールバック

このプログラムは、 新規バッファーイベントに関連する情報にアクセスする方法を示します。CallbackMultiThreadを使用していますが、ほかのコールバックメソッドを使うこともできます。

                        
                            #include <iostream>  
#include <EGrabber.h>  

using  namespace Euresys;

class MyGrabber : public EGrabber<CallbackMultiThread> {
    public:
        MyGrabber(EGenTL &gentl) : EGrabber<CallbackMultiThread>(gentl) {
            runScript("config.js");
            enableEvent<NewBufferData>();
            reallocBuffers(3);
            start();
        }

    private:
        virtual  void onNewBufferEvent(const NewBufferData &data) {
            ScopedBuffer buf(*this, data);                                      // 1  
            uint64_t ts = buf.getInfo<uint64_t>(GenTL::BUFFER_INFO_TIMESTAMP);  // 2
            std::cout << "event timestamp: " << data.timestamp << " us, "         // 3
                      << "buffer timestamp: " << ts << " us" << std::endl;
        }                                                                       // 4
};

int main() {
    try {
        EGenTL gentl;
        MyGrabber grabber(gentl);
        while (true) {
        }
    } catch (const std::exception &e) {
        std::cout << "error: " << e.what() << std::endl;
    }
}
                    
onNewBufferEventに、一時的なScopedBufferオブジェクトbufを作成します。ScopedBufferコンストラクターは次の2つの引数を取ります。
バッファーを所有しているグラバー: EGrabberから派生したクラス内で処理しているため、単に*thisを渡します。
バッファーに関する情報: これはdataで提供されています。
カメラがフレームグラバーへのデータ送信を開始した時刻として定義されているバッファーのタイムスタンプを取得します。
イベントIDに関するセクションで説明したように、新規バッファーイベントはほかの種類のイベントとはわずかに異なり、これらのイベントはGenTLの標準であり、関連付けられたnumidはありません。
そのため、onNewBufferEventに渡されたNewBufferData構造にはnumidフィールドがありません。ただし、バッファーへのデータ転送が完了したことがドライバーに通知された時刻を示すtimestampフィールドがあります。このevent timestampは、手順2で取得したbuffer timestampよりも必然的に長くなります。
ローカル変数bufが作成されたブロックの終わりに達します。範囲外となり、破棄されます(ScopedBufferデストラクターが呼び出されます)。これにより、bufに含まれているGenTLバッファーがグラバーのデータストリームのキューに戻されます。

プログラムの出力例:

                    event timestamp: 77185931621 us, buffer timestamp: 77185919807 us
 event timestamp: 77185951618 us, buffer timestamp: 77185939809 us
 event timestamp: 77185971625 us, buffer timestamp: 77185959810 us
 event timestamp: 77185991611 us, buffer timestamp: 77185979812 us
 event timestamp: 77186011605 us, buffer timestamp: 77185999808 us
 event timestamp: 77186031622 us, buffer timestamp: 77186019809 us
 event timestamp: 77186051614 us, buffer timestamp: 77186039810 us
 event timestamp: 77186071611 us, buffer timestamp: 77186059811 us
 event timestamp: 77186091602 us, buffer timestamp: 77186079812 us
 event timestamp: 77186111607 us, buffer timestamp: 77186099814 us
                

関連ファイル

include/EGrabber.h メインヘッダー。include/RGBConverter.hを除くすべてのヘッダーを含みます。Euresys::EGrabberEuresys::BufferEuresys::ScopedBufferを定義します。
include/EGrabberTypes.h Euresys::EGrabberに関連するデータ型を定義します。
include/EGenTL.h Euresys::EGenTLを定義します。
include/GenTL_v1_5.h 標準GenTLヘッダー。標準の型、関数、および定数を定義します。
include/GenTL_v1_5_EuresysCustom.h eGrabber固有の定数を定義します。
include/RGBConverter.h Euresys::RGBConverterヘルパークラスを定義します。