DFG/HDMIの監視と再接続処理
背景
デバイスハードウェアのハングアップなどでフレームが停止し、アプリケーションの再起動などでデバイスの再復帰などができず、デバイスへの電源再起動でしか復帰できない場合があります。ICImagingControl4.0(以下IC4)では、USBデバイスをカーネルレベルで再起動するAPI(Device Reset)を用意しています。このAPIをエラー処理として用いる事によってUSBデバイスの半永久的な安定動作を実現します。
概要
DFG/HDMIをライブスタート後にフレームが停止しているかどうかを500ms毎に監視し、フレーム停止を検知したら自動でデバイスの再接続を行う、というエラー処理を実装しています。エラー処理は、IC4のDevice Resetを用いていますが、DFG/HDMIのデバイス制御~データストリーム出力のメインスレッドはIC3.5で制御しています。ICl3.5の既存プログラム開発資産を用いながら、IC4の新規機能をエラー処理として追加することができます。
サンプルプログラム
利用した開発環境 | Visual Studio™ 2022 |
---|---|
SDK | IC Imaging Control 3.5(Python, C#, VB.NET), IC Imaging Control 4 SDK |
デバイスドライバ | Cam33U_setup GenTL Producer for USB3 Vision Cameras |
デバイス | DFG/HDMI |
サンプル(C#) | Recovery_DFGHDMI_cs_3.5.zip |
サンプル(VB.NET) | ー |
exeファイル アプリケーション |
ー |
別途ファイル | ー |
関連参照URL | ー |
サンプルプログラムの外観
解説
初期設定(IC3.5)
private void Form1_Load(object sender, EventArgs e)
{
icImagingControl1.Device = "DFG/HDMI";
icImagingControl1.VideoFormat = "RGB32 (1920x1080)";
ic4.Library.Init();
grabber = new ic4.Grabber();
icImagingControl1.LiveDisplay = false;
_frameQueueSink = new FrameQueueSink(Callback_FrameQueueSink, MediaSubtypes.RGB32, 5);
icImagingControl1.Sink = _frameQueueSink;
if (icImagingControl1.DeviceValid)
icImagingControl1.LiveStart();
else
Close();
}
ここではフォームが読み込まれたとき(Form1_Loadイベント)に、DFG/HDMIをオープンし、ライブスタートをしています。まず、Deviceプロパティで使用するデバイス名(カメラやキャプチャボード)、VideoFormatで解像度とカラーフォーマットを指定します。次に、IC4でデバイスのリセットするためにIC4ライブラリの初期化(ic4.Library.Init())を行い、IC4でカメラ制御ができるようにしておきます。その後、FrameQueueSinkを使ってフレームの取得処理をコールバック関数Callback_FrameQueueSinkで処理するようにします。最後に、DeviceValidプロパティでデバイスが正しく接続されたかどうかを確認し、成功していればicImagingControl1.LiveStart()によりライブスタートをします。
フレームの表示処理(IC3.5)
private FrameQueuedResult Callback_FrameQueueSink(IFrameQueueBuffer buffer)
{
icImagingControl1.DisplayImageBuffer(buffer);
return FrameQueuedResult.ReQueue;
}
上記はFrameQueueSinkにフレームが届いた際に呼び出されるコールバック関数です。icImagingControl1.DisplayImageBuffer(buffer)を使用することで、受信したフレームを画面(フォーム)上に表示しています。
CSVログ出力処理
void LogToCsv(string message)
{
string logFilePath = "camera_log.csv";
string line = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff},{message}";
File.AppendAllText(logFilePath, line + Environment.NewLine, new UTF8Encoding(true));
}
指定されたメッセージをCSVファイル形式でログとして記録するための処理です。具体的には、引数messageに渡された文字列を、現在の日時(タイムスタンプ)とともに、下記のようにcamera_log.csvというファイルに記載します。
例:
2025-07-04 15:23:45.123, message
フレーム停止検出のタイマー(IC3.5)
// クラスのメンバ変数として以下を宣言
private ulong _lastFramesDelivered = 0;
private int _noChangeCount = 0; // 変化なしのカウント
private const int MaxNoChangeTicks = 1; // 例:1回連続で変化がなければフラグを立てる
private bool _isFrameStopped = false;
private void timer1_Tick(object sender, EventArgs e)
{
if (icImagingControl1.DeviceValid && icImagingControl1.LiveVideoRunning)
{
ulong currentFramesDelivered = icImagingControl1.DriverFrameDropInformation.FramesDelivered;
if (currentFramesDelivered == _lastFramesDelivered)
{
_noChangeCount++;
if (_noChangeCount >= MaxNoChangeTicks)
{
_isFrameStopped = true;
exceptionError1();
exceptionError2();
}
}
else
{
_noChangeCount = 0;
_isFrameStopped = false;
}
_lastFramesDelivered = currentFramesDelivered;
}
}
この処理は一定間隔(500ms毎)で呼び出されるタイマーイベントで、カメラの映像が止まっていないかを監視するために使われています。まず、カメラが正しく接続されていて、ライブスタート中であることを確認します(DeviceValidとLiveVideoRunningが両方ともtrueのときのみ処理を実行)。次に、現在までにドライバから送られたフレーム枚数を(FramesDelivered)として取得し、前回記録していたフレーム枚数(_lastFramesDelivered)と比較します。フレーム枚数が変わっていなければ「映像が止まっている」と判断し、 _noChangeCount を500ms毎にカウントアップします。そして、その回数が MaxNoChangeTicks(しきい値=1)を超えると、_isFrameStoppedをtrueにして、再接続処理exceptionError1()を呼び出します。このように、カメラからの映像が一定時間止まったままになった場合に、自動的に再接続処理を行うようにしています。
エラー処理1(IC4.0でエラー処理、IC3.5で再オープン)
private void exceptionError1()
{
grabber.DeviceOpen("DFG/HDMI");
if (!grabber.IsDeviceValid)
{
LogToCsv("exceptionError1 DeviceOpen 失敗: DFG/HDMI デバイスが見つかりません。");
return;
}
grabber.DeviceClose();
LogToCsv("exceptionError1 DeviceClosed");
try
{
if (icImagingControl1.LiveVideoRunning)
icImagingControl1.LiveStop();
icImagingControl1.Device = "DFG/HDMI";
icImagingControl1.VideoFormat = "RGB32 (1920x1080)";
icImagingControl1.LiveStart();
LogToCsv("exceptionError1 LiveStart 再開");
}
catch (Exception reconnectEx)
{
LogToCsv("exceptionError1 再接続中にエラー: " + reconnectEx.Message);
}
}
exceptionError1()関数は、カメラの映像が停止したと判断された場合に、「IC4でデバイス再接続処理を行うための関数」です。 まず、IC4のAPIを用いてgrabber.DeviceOpen("DFG/HDMI")を実行し、デバイスが正常に開けるかを確認し、grabber.DeviceClose()によってデバイスを閉じます。 次に、IC3.5を使って、icImagingControl1.Device = "DFG/HDMI"でデバイスをオープンし、LiveStartを呼び出すことでIC3.5のライブ映像を再開します。IC4での再接続処理を行い、素早くIC3.5のデバイスオープン⇒ライブスタートでライブスタートを再開します。
エラー処理2(IC4.0でエラー処理、IC3.5で再オープン)
private void exceptionError2()
{
grabber.DeviceOpen("DFG/HDMI");
if (!grabber.IsDeviceValid)
{
LogToCsv("exceptionError2 DeviceOpen 失敗: DFG/HDMI デバイスが見つかりません。");
return;
}
var sink = new ic4.SnapSink();
grabber.StreamSetup(sink);
// Reset
try
{
grabber.DevicePropertyMap.ExecuteCommand("DeviceReset");
}
catch
{
}
try
{
grabber.DeviceClose();
LogToCsv("exceptionError2 DeviceClosed");
while (!icImagingControl1.DeviceValid)
{
try
{
icImagingControl1.Device = "DFG/HDMI";
icImagingControl1.VideoFormat = "RGB32 (1920x1080)";
LogToCsv("DeviceOpen 成功");
}
catch
{
System.Threading.Thread.Sleep(100);
}
}
if (icImagingControl1.LiveVideoRunning)
icImagingControl1.LiveStop();
icImagingControl1.LiveStart();
LogToCsv("exceptionError2 LiveStart 再開");
}
catch (Exception reconnectEx)
{
LogToCsv("exceptionError2 再接続中にエラー: " + reconnectEx.Message);
}
}
exceptionError2()関数は、exceptionError1()「IC4でデバイス再接続処理を行うための関数」でもデバイスが復帰しない場合のエラー処理としての「IC4でストリーム取得とDeviceResetを行うための関数」です。
まず、IC4のAPIであるgrabber.DeviceOpen("DFG/HDMI")を使用してカメラデバイスを開き、ストリームを初期化します。次に、grabber.DevicePropertyMap.ExecuteCommand("DeviceReset")を実行し、ハードウェアリセットを行います(このとき、デバイスマネージャー上で一時的にカメラが認識されなくなります)。その後、grabber.DeviceClose()によってデバイスを閉じ、icImagingControl1.Deviceでデバイスが再び認識されるまで接続を試みます。正常に再接続できた場合は、icImagingControl1.LiveStart()を実行し、ライブ映像の再表示を行います。このようにして、USB接続のキャプチャデバイスが一時的にフリーズした場合でも、自動的に復旧処理を行うことが可能になります。
<重要なポイント>
exceptionError1()とexceptionError2()の両方をエラー処理として実装することで、以下の4つのコマンドがDFG/HDMIのハードウェアに届きます。
- IC4でのデバイスオープン処理 ← exceptionError1()
- IC4でのデバイスクローズ処理 ← exceptionError1()
- IC4でのストリーム取得処理 ← exceptionError2()
- IC4でのDeviceReset処理 ← exceptionError2()
exceptionError1()だけのエラー処理の場合は、復帰まで2秒以内に完了します。
exceptionError2()だけのエラー処理の場合は、復帰まで4秒以内で完了します。
基本的にexceptionError1()のみでDFG/HDMIは復帰します。しかしUSBデバイスのハングアップの原因は特定が困難です。exceptionError2()まで組み込むと、デバイスへのストリーム取得とDeviceReset処理も含めて4種類のコマンドがハードウェアに実行されています。
ボタン押下時(IC4 LiveStart)
private void btnIC4LiveStart_Click(object sender, EventArgs e)
{
LogToCsv("ボタン押下");
grabber.DeviceOpen("DFG/HDMI");
if (!grabber.IsDeviceValid)
{
LogToCsv("DeviceOpen 失敗: DFG/HDMI デバイスが見つかりません。");
return;
}
var sink = new ic4.SnapSink();
grabber.StreamSetup(sink);
grabber.StreamStop();
grabber.DeviceClose();
}
ここでは、IC4のAPIを使用して一時的にカメラデバイスへ接続し、ライブスタートを実行しています。これにより、IC3.5側で使用中だったカメラとの接続が強制的に解除され、意図的にエラー状態を発生させることができます。この処理は、再接続処理用の関数(exceptionError1()やexceptionError2())の挙動を確認・検証する目的で使用します。