Euresys::Eグラバー

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

Euresys::EGrabber C ++クラスの上に構築された.NETアセンブリも提供されています。このドキュメントでは、主にC ++ APIに焦点を合わせます。C ++インターフェイスと.NETインターフェイスの間の小さな違いは、 専用の章をご確認ください。

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

#include <EGrabber.h>

Euresys::EGrabberはヘッダのみのライブラリです( lib または dll ファイルとしては提供されていません2)。それはいくつかのクラスで構成され、そのうち最も重要なクラスは次の 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. Euresys::EGrabber クラスを定義し、必要なその他のヘッダーファイル( EGenTL.h や標準の GenTL header fileファイルなど)を含む EGrabber.hを含めます。

  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 は、 interface moduleからの回答が必要であることを示しています。

  5. 同様に、デバイスのIDを調べます。今回は、 Euresys::DeviceModule を使用し device moduleをターゲットにします。

  6. 最後に、カメラの解像度を読みます。Euresys::RemoteModule は、値をカメラから取得する必要があることを示します。

  7. Euresys::EGrabberはエラーを報告するために例外を使用しているので、コードを try ... catch ブロックで囲みます。

プログラム出力例

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

画像の取得

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

#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 関数はデータストリームを開始するために呼び出されます。

    この例では、カメラとフレームグラバーが正しく設定されていると仮定しています。実際のアプリケーションでは、集録を開始する前(そしてそのことに関するバッファを割り当てる前)に configuration script を実行した方が安全です。これは別例でも示します。

  4. グラバーでいっぱいになるまでバッファーを待ちます。結果は Euresys::ScopedBufferスコープ という用語は、バッファの存続期間が現在のスコープ(つまり、現在のブロック)であることを示すために使用されます。

  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のタイプに合わせて getInfo バージョンの uint64_t を使用します。

    注:Coaxlinkの場合、タイムスタンプは常に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は次のカメラサイクルの準備ができています)
  • ...

イベントのe 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. enableAllEvents と呼ばれるヘルパー関数を定義し、引数としてモジュール(またはポート) pを取ります。

  2. $ee 関数を使用して、 EventSelector が取ることができる値のリストを取得します。これはモジュール pによって生成されたイベントのリストです。(eeenumエントリを表します。)

  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アセンブリについての章で確認できます。

イベントの識別

アプリケーションにイベントが通知されると、実行されるコールバック関数はそのイベントのカテゴリを示します。発生した実際のイベントは、 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 が必要ありません。

  • 単純なネーミング規則に従います: カテゴリ という名前のイベントのカテゴリには、引数としてSomeCategoryData構造を取り、共通の EVENT_DATA_NUMID_ プレフィックスnumid として EVENT_DATA_NUMID_SOME_CATEGORY_ を使用する onSomeCategoryEvent という名前のコールバック関数があります。

イベントとコールバックを説明するいくつかの完全な サンプルプログラム をすぐに示しますが、それを実行する前に説明する必要があるもう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. これ 使用 指示は 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. EGrabber<CallbackOnDemand>ではなく MyGrabberEGrabber<CallbackSingleThread> から派生しています。

  2. そのため MyGrabberコンストラクタは、 EGrabber<CallbackSingleThread>コンストラクタを呼び出すことによってその基本クラスを初期化します。

  3. EGrabberprocessEventを呼び出すコールバックスレッドを作成します。ここでは、単純に無限ループに入ります。

ご覧のとおり、 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;
    }
}
  1. onNewBufferEventで、一時的な ScopedBuffer オブジェクト bufを作成します。ScopedBuffer コンストラクターは2つの引数を取ります:

    • バッファを所有しているグラバー: EGrabberから派生したクラスに属しているので、単純に *thisを渡します。
    • バッファに関する情報:これは dataで提供されています。
  2. カメラがフレームグラバーへのデータ送信を開始した時刻として定義されているバッファのタイムスタンプを取得します。

  3. イベント識別に関するセクションで説明したように、 新規バッファ イベントは他の種類のイベントとはわずかに異なります:それらは標準であり(GenTLによると)、関連する numidはありません。

    結果として、 onNewBufferEvent を通った NewBufferData 構造は numid フィールドを持ちません。ただし、 バッファへのデータ転送が完了したことがドライバに通知された時刻を示す timestamp フィールドがあります。この イベントタイムスタンプ は、手順2で取得した バッファタイムスタンプ よりも必然的に長くなります。

  4. ローカル変数 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::EGrabber, Euresys::Buffer, Euresys::ScopedBufferを定義する。
include/EGrabberTypes.h Euresys::EGrabberに関連するデータ型を定義します。
include/EGenTL.h Euresys::EGenTLを定義します。
include/GenTL_v1_5.h 標準GenTLヘッダー。標準の型、関数、および定数を定義します。
include/GenTL_v1_5_EuresysCustom.h Coaxlink固有の定数を定義します
include/RGBConverter.h Euresys::RGBConverter ヘルパークラスを定義します