1

さて、私が高レベルで行っていることは、システムに接続されているすべての VISA デバイスをシステムでスキャンし、それらを識別させることです。

問題は、すべての VISA デバイスが自分自身を識別する機能をサポートしているわけではないことです。私が知っている唯一の方法は、デバイスにそれを行うように指示することです。これにより、自分自身を識別できないものは、最小 1 秒のタイムアウトに依存するようになります。タイムアウトを待っている間、タイムアウトが完了するまで TCL スクリプトと Wish アプリケーションがフリーズします。複数のデバイスを使用すると、何が起こっているのかをユーザーに更新することができず、数秒かかる可能性がある厄介な待機時間が残ります。

これが私のコードです:

proc ::VISA::Scan {} {
    # Open a temporary resource manager
    set TemporaryResourceManagerId [::visa::open-default-rm]

    # Get addresses for all devices on system
    foreach address [::visa::find $TemporaryResourceManagerId "?*"] {

        # Create temporary VISA channel
        set TemporaryChannel [visa::open $TemporaryResourceManagerId $address]

        # Have device identify itself while suppressing errors
        if {![catch {puts $TemporaryChannel "*IDN?"}]} {
            if {![catch {gets $TemporaryChannel} result]} {
                if {![string is space $result]} {
                    puts $address
                    puts "$result \n"
                }

                # Clear any potential errors
                puts $TemporaryChannel "*CLS"
            }
        }

        # Destroy temporary channel
        close $TemporaryChannel
        unset TemporaryChannel
    }

    # Destroy temporary resource manager
    close $TemporaryResourceManagerId
    unset TemporaryResourceManagerId
}

照会するデバイスのタイプを知る方法がないため、TCL 側でこれを防ぐ方法があるかどうか疑問に思っていました。スクリプトのいくつかの異なる場所で「update」と「update idletasks」を使用してみましたが、フリーズの間に少し時間がかかるだけです。

どんな助けでも大歓迎です。前もって感謝します!

4

3 に答える 3

2

これを行う標準的な方法は、I / Oチャネルを非ブロッキングに設定し、fileeventまたはを使用してtclのイベントループを使用することchan eventです。ただし、tclvisaのドキュメントには、fileeventはvisaチャネルではサポートされていないと記載されています。

したがって、次善の策は、非ブロッキングI / O(タイムアウトを0に設定するだけ)を使用し、ビジーループでチャネルを読み取るか、遅延後に読み取ることです。これらのいずれかは、散水するのではなく、イベントループで処理する必要がありますupdate(これには望ましくない副作用があります)。

したがって、busyloopに対して、次のようなことを行うことができます。

proc busyread {v n} {
    if {$::readdone == 1} {set ::$n "Error"}    
    set r [visa::read $v]
    if {$r == ""} {
        after 5 [list busyread $v $n]
    } else {
        set ::$n $r
        set ::readdone 1
    }
}


set f [visa::open ...]
fconfigure $f -blocking 0
after 1000 [list set ::readdone 1]
set ::readdone 0
busyread $f result
vwait ::readdone   
# $result will now be either the result, or "Error"

readこれは、空に戻り続ける限り、継続的にスケジュールを変更します。

これは、より大きなGUIプログラム内で機能するように少し再構築する必要があります(vwaitとtimeoutは異なる方法で実行する必要があります)が、これは基本的な方法を示しています。

于 2013-01-09T21:10:02.563 に答える
2

タイムアウトを非同期に使用afterして処理する必要があります。fileevent特に Tcl8.6 より前の場合は、それほど簡単ではありません。プロシージャをイベント ハンドラの束に分割し、必要なすべての情報をそれらに渡す必要があります。

タイムアウト ハンドラをスケジュールします。

proc handleTimeout {channel} {
    ....
    close $channel
    .... # or do some other thing, 
    .... # but don't forget to remove fileevent handler if not closing!
}
....
after 1000 [list handleTimeout $TemporaryChannel]

チャネルをノンブロッキングにし、fileevent ハンドラーをインストールします。

proc tryGetsIDN {channel} {
    if {[gets line]!=-1} {
        # We have an answer!
        # Cancel timeout handler
        after cancel [list handleTimeout $TemporaryChannel]
        ....
    }
}
....
fconfigure $TemporaryChannel -blocking 0
fileevent $TemporaryChannel readable [list tryGetsIDN $TemporaryChannel]

最も難しい部分: GUI イベントを適切に処理することを確認してください。たとえば、すべての非同期ハンドラーをキャンセルする「キャンセル」ボタンがある場合は、チャネルを閉じてタイムアウト ハンドラーをキャンセルするようにしてください (ここでは、チャネルとハンドラーの追加の簿記が必要になる場合があります)。

Tcl 8.6 では、コルーチンを使用して、手順を協調的な「バックグラウンド スレッド」として機能させることができます。コルーチンから生成され、完了またはタイムアウト時に再入力する「タイムアウト付きの取得」を実装するのは簡単です。ただし、すぐに使用できるソリューションはまだありません。

于 2013-01-09T20:45:00.413 に答える
1

私は実際に、問題の tclvisa 側で解決策を見つけました。組み込みの tclvisa コマンドを使用するよりも、チャネルのタイムアウトを指定するためのより良い方法を見つけました。

fconfigure $TemporaryChannel -timeout 100

このタイムアウトを設定しても問題が完全に解決されるわけではありませんが、あいまいなところまで軽減されます。すべての応答に感謝します!

于 2013-01-09T22:16:12.973 に答える