第 4 章 チュートリアル 20
4.3 バックグランド処理
ここでは、バックグランド処理によるデジタル出力処理を行います。FbiDio.DLLにはバックグラ ンド処理を行うための、バックグランド関数が用意されています(関数名に〜Backとあるものがこ れにあたります)。
バックグランド関数は、処理回数を指定することにより定期的に指定した間隔で処理を繰り返す 関数です。一定時間ごとに入出力関数をコールして実現する処理をバックグランド関数1度のコー ルにより実現できます。これらバックグランド関数はオーバーラップ構造体(後に記載)を指定する ことで非同期に動作します。
Visual Basicにおいて1つのプロジェクト内で非同期処理を実現するには、
・ActiveX EXE を作成しプロジェクトに組み込む
・Win32APIを使用する
等がありますが、ここではWin32APIを使用する方法を記載します。
では、OUT1に接続されたLEDをバックグランドで点滅させるプログラムを作成して行きます。
Step1. 基本フォーム作成
ここでは、これから作成するプログラムの画面作成を行います。 (詳しくは『21ページ 4.1 デジ タル入力』でのStep1〜Step3を参考にしてください。)
1. Visual Basicを起動し新しい標準 EXE プロジェクトを作成します。
2. プロジェクト名を OutputBack に変更します。
3. 既定のフォームの名前を formOutputBack に変更し、フォームのタイトルを"バックグランド 出力"に変更します。
4. プロジェクトに標準モジュールを追加します。
5. 標準モジュールに 標準モジュールのプロシージャ宣言のコード ( 『28ページ Step2. DLLプ
ロシージャ宣言』 )を追加します。
Step2. フォアグラウンド処理の作成
ここでは、バックグランド処理と対比するための、フォアグラウンドで実行させるプログラムの 作成を行います。
1. フォームにタイマー(Timer)コントロールを描画し、オブジェクト名をtimForegroundにします。
2. timForegroundのIntervalプロパティを10に設定します。
3. 作成したタイマーコントロール(timForeground)をダブルクリックし編集ウィンドウを開き以下 のコードを記述します。
(
タイマーコントロール
(timForeground)のコード
) Private Sub timForeground()Static Xrev As Boolean Static Yrev As Boolean Static XPos As Long Static Ypos As Long
ScaleMode = 3 'ScaleMode をピクセルに設定します。
DrawWidth = 10 'DrawWidth を設定します。
PSet (XPos, Ypos), &H8000000F '前回描画したドットを消す '表示位置算出(X 軸)
If Xrev Then
XPos = XPos ‑ DrawWidth If XPos < 0 Then Xrev = False Else
XPos = XPos + DrawWidth
If XPos > ScaleWidth Then Xrev = True 'ベクトル反転 End If
'表示位置算出(Y 軸) If Yrev Then
Ypos = Ypos ‑ DrawWidth If Ypos < 0 Then Yrev = False Else
Ypos = Ypos + DrawWidth
If Ypos > ScaleHeight Then Yrev = True 'ベクトル反転 End If
'ドット描画
PSet (XPos, Ypos), 0 End Sub
- 59 - Interface Corporation
Step3. バックグランド処理の作成
ここでは、バックグランドでデジタル出力を行うプログラムの作成を行います。バックグランド での非同期処理を実現するために、ここではWin32 APIに定義される以下の関数、および構造体を 使用しています。
・CreateEvent関数
イベントオブジェクトの作成を行います。
・Overlapped構造体
非同期I/O制御に必要な構造体です。
・WaitForSingleObject関数
オブジェクトがシグナル状態になるまで待機します。
・CloseHandle関数
オブジェクトハンドルのクローズを行います。
各関数および構造体の詳細はMicrosoft社提供のWin32APIリファレンスか、もしくはその他専門書 籍を参照してください。
バックグランド処理の構築は、以下のシーケンスで行います。
非同期に実行されるオブジェクトを作成す る。
↓
バックグランドで行う処理の定義する。
↓
バックグランド処理を開始する。
↓
バックグランドの処理を監視し、処理が終了 した場合、作成したイベントを解放する。
では、実際にプログラムの作成を行います。
1. フォーム上にコマンドボタン(CommandButton)コントロールを描画し、 cmdBackOut と名付けコ マンドボタンコントロールのタイトルを "出力" に変更します。
2. 作成したコマンドボタン(cmdBackOut)をダブルクリックし編集ウィンドウを開き以下のコード を記述します。
(
コマンドボタン
(cmdOutputBack)のクリックイベントのコード
) Private Sub cmdBackOut̲Click()
Dim lpszName As String Dim dwStartNum As Long Dim nRet As Long Dim pnBuffer(20) As Long Dim lpOverlapped As OVERLAPPED Dim lpEventAttributes As Long Dim I
‘ボードの初期化
lpszName = "FBIDIO1" & Chr(0)
hDeviceHandle = DioOpen(lpszName, FBIDIO̲FLAG̲SHARE)
If hDeviceHandle = ‑1 Then
MsgBox ("デバイスのオープンに失敗しました") Exit Sub
End If
cmdBackOut.Enabled = False ‘出力ボタンを使用禁止にします。
'イベントオブジェクトの作成を行います。
lpOverlapped.hEvent = CreateEvent(lpEventAttributes, True, False, 0) 'バックグランド処理の設定を行います
hBackGroundHandle = DioSetBackGround(hDeviceHandle, 1, 1, 20, 200, 20, 0) If hBackGroundHandle = &HFFFF Then
MsgBox ("バックグランドの設定に失敗しました") Exit Sub
End If
'出力するデータを変数に格納します。
For I = 0 To 19 If I Mod 2 = 0 Then pnBuffer(I) = 1 Else
pnBuffer(I) = 0 End If
Next
'バックグランド出力を開始します
nRet = DioOutputPointBack(hDeviceHandle, hBackGroundHandle, pnBuffer(0), 80, lpOverlapped) If nRet <> &HC0000008 Then
MsgBox ("データの出力に失敗しました " + Str(nRet)) End If
'イベントがシグナル状態になるまで待機します。
Do Until WaitForSingleObject(lpOverlapped.hEvent, 0) = 0 DoEvents
Loop
- 61 - Interface Corporation
cmdBackOut.Enabled = True ‘出力ボタンを使用可能にします。
'バックグランドでの設定を解放します
nRet = DioFreeBackGround(hDeviceHandle, hBackGroundHandle) ‘作成したイベントオブジェクトをクローズします。
CloseHandle (lpOverlapped.hEvent) '終了処理
nRet = DioClose(hDeviceHandle) If nRet <> 0 Then
MsgBox ("デバイスのクローズに失敗しました") End If
End Sub
ここで、プログラムを実行してみて下さい。
フォーム上で点(ドット)が移動しています。このとき、「出力」ボタンをクリックして下さい。
LEDが点灯すると同時に、フォーム上の点(ドット)の描画が、停止することなく移動しつづけてい ることが確認できると思います。これが、非同期処理です。
「出力」ボタンのクリック LEDの点滅処理
ドットの移動処理
開始 終了
また、プログラム中にDioOutputPointBack関数の正常終了時の戻り値を&HC0000008としている
のは、 Visual Basicより同関数を使用した場合、関数呼び出しと時に非同期I/Oが実行されるので結
果として、エラーコード&HC0000008(FBIDIO_ERROR_IO_PENDING)が返されるためです。
<備考>
使用するコンピュータのスペックによっては、バックグランド処理中にフォームの最小化等を行 うと、バックグランド処理が不安定になる(200msごとに点滅しなくなる)場合があります。このよ うな場合、プログラムをEXEファイルにコンパイルした後実行すると、回避できます。
それでは対比のために、プログラム中に同期処理を行うコード作成し、非同期の場合との動作の
違いを確認してみましょう。
リスト中ほどの
' シグナル状態になるまで待機(Win32API)
Do Until WaitForSingleObject(lpOverlapped.hEvent, 0) = 0 DoEvents
Loop
の部分を
' シグナル状態になるまで待機(Win32API)
nRet = WaitForSingleObject(lpOverlapped.hEvent, ‑1)
と修正すると、この関数が同期処理されます。
プログラム実行後、「出力」ボタンをクリックすると、LEDが点灯すると同時に、フォーム上の 点(ドット)の描画が停止します。これは、コマンドボタンコントロール(cmdBackOut)に記述したプ ログラムとタイマーコントロール(timForeground)に記述したプログラムがWaitForSingleObject関数 呼び出しの部分で同期していることを意味します。つまりは、バックグランドの処理が完了しな いとVisual Basic側に制御が戻りません。
したがって、 コマンドボタン(cmdOutputBack)のクリックイベント ( 『59ページ Step3. バック グランド処理の作成』)でのコード生成したイベントオブジェクト(データ出力イベント)が完了す ると、処理がVisual Basicに戻り、再びフォーム上の点(ドット)が移動し始めます。
「出力」ボタンのクリック LEDの点滅処理
ドットの移動処理
開始 終了
ここより、使用したFbiDio.DLLの関数について記載します。
プログラムを再度実行し、今度はLEDの点灯に着目して下さい。 LEDは200ms間隔で点灯と消灯を
繰り返しています。これらバックグランドで行う処理を設定するにはDioSetBackGround関数を使
用します。
- 63 - Interface Corporation
hBackGroundHandle = DioSetBackGround(hDeviceHandle, 1, 1, 20, 200, 20, 0)
ハンドル番号が返されます。
ボードのオープン時に取得したデバイスハ ンドルを指定します。
将来拡張用です。0を指定して下さい。
制御(監視)する接点番号の開始番号を指定します。
制御(監視)する開始点番号より処理対象とする点数を指定します。
制御データ(取得データ)を格納するバッファのサイズを指定します。
「使用方法」
動作させる周期を ms単 位で指 定 し ます。
処理を実行する回 数を指定します。
コマンドボタン(cmdOutputBack)のクリックイベント ( 『59ページ Step3. バックグランド処理 の作成』)では出力接点番号1(OUT1)より、配列変数pnBuffer()に格納された情報を200msごとに出 力する設定を行っています。
第6引数の実行回数ですが、pnBuffer(0)〜pnBuffer(19)までの出力を1セットとし、20セット繰り返 すという意味ではありません。 pnBuffer(0)が処理されると1回、 200ms後pnBuffer(1)が処理されると 1回、というように200msごとに処理される回数をカウントし、その合計回数を指定します。よっ て、 コマンドボタン(cmdOutputBack)のクリックイベント ( 『59ページ Step3. バックグランド 処理の作成』 )でではバッファ内は20回分のデータしか格納されていませんから、実行回数の上限
は 20となります。20以上の値を指定した場合、21回目はpnBuffer(0)が実行されます。
反対に20以下の値、例えば10を設定すると、pnBuffer(9)を出力し、そこで処理は完了します。試 しに、バックグランド処理の設定を下記コードのように修正し実行してください。
(
バックグランド処理設定のコード
) ' バックグランド処理の設定を行いますhBackGroundHandle = DioSetBackGround(hDeviceHandle, 1, 3, 5, 200, 5, 0)
出力接点番号1(OUT1)から出力接点番号3(OUT3)の状態が以下のよう変化します。
経過時間 OUT1 OUT2 OUT3 実行回数
0〜200ms 1 0 1 1回目
200ms〜
400ms
0 1 0 2回目
400ms〜
600ms
1 0 1 3回目
600ms〜
800ms
0 1 0 4回目
800ms〜1s 1 0 1 5回目
この時、処理の対象となっているのはpnBuffer(0)〜pnBuffer(14)までです。
ここまでで、バッファに格納されるデータと実行回数の関係は理解頂けたと思います。
次に実際の出力処理ですが、バックグランドでの出力にはDioOutputPointBack関数を使用します。
DioSetBackGround関数でバックグランド処理を設定後、 DioOutputPointBack関数を呼び出すこと により始めてバックグランドでの処理が開始されます。
nRet = DioOutputPointBack(hDeviceHandle, hBackGroundHandle, ̲ pnBuffer(0), 80, lpOverlapped)
ハンドル番号が返されます。
ボードのオープン時に取得したデバイ スハンドルを指定します。
DioSetBackGround関数で取得したバック グランド処理ハンドルを指定します。
デバイスへ出力するデータバッファを指定
します。 デバイスに出力するバイト数を指定しま
す。
OVERLAPPEDデータ構造体へのポ インタを指定します。
「使用方法」
第4引数のデバイスに出力するバイト数は下式のよう算出します。
nNumberOfBytesToWrite = dwPointNum × dwValueNum × 4
- 65 - Interface Corporation dwPointNumとdwValueNumはDioSetBackGround関数で設定した値を指定します。最後の 4ですが、
実際のDLLの仕様では、C言語でのint型のバイナリサイズ(sizeof(int))が定義されています。Visual Basicでは、C言語でのint型を長整数型(Long)として評価しますのでVsual Basicでの長整数型(Long) のバイナリサイズをあてはめ4としています。
バックグランド処理設定のコード (『59ページ Step3. バックグランド処理の作成』)では、
nNumberOfBytesToWrite = 1 × 20 × 4 = 80となります。
また、 DioSetBackGroundで設定したバックグランド処理の設定は、バックグランド処理が終了ま た は 不 要 に な っ た 場 合 、 こ れ を 解 放 し な け れ ば な り ま せ ん 。 バ ッ ク グ ラ ン ド 処 理 を DioStopBackGround関数で停止した場合にも、処理停止後、設定の解放が必要となります。
バックグランド処理の設定の解放には、DioFreeBackGround関数を使用します。
Dim pbyValue As Byte
nRet = DioFreeBackGround(hDeviceHandle, hBackGroundHandle)
ボードのオープン時に取得したデバ イスハンドルを指定します。
関数が失敗するとエラーコー ドが格納されます。
DioSetBackGround 関数で取 得したバックグランド処理 ハンドルを指定します