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 方面防止这种情况发生,因为我无法知道我将查询什么类型的设备。我已经尝试在脚本的几个不同位置使用“更新”和“更新空闲任务”,但它只是让我在冻结之间有一点时间。

任何帮助,将不胜感激。提前致谢!

4

3 回答 3

2

您必须使用afterfileevent异步处理超时。这并不容易,尤其是在 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]

使通道非阻塞,安装一个文件事件处理程序:

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 回答
2

执行此操作的标准方法是通过将 I/O 通道设置为非阻塞并使用fileevent或使用 tcl 的事件循环chan event;但是,tclvisa 文档指出,签证通道不支持 fileevent。

因此,下一个最好的方法是使用非阻塞 I/O(它只是将超时设置为 0)并使用busyloop 读取通道或在延迟后读取它;这些中的任何一个都应该通过事件循环来处理,而不是通过洒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 和 timeouts 需要以不同的方式完成),但这显示了基本方法。

于 2013-01-09T21:10:02.563 回答
1

实际上,我在问题的 tclvisa 方面找到了解决方案。我找到了一种更好的方法来指定通道的超时,而不是使用我错误地认为我必须使用的内置 tclvisa 命令。

fconfigure $TemporaryChannel -timeout 100

设置此超时并不能完全解决问题,但会将其减少到晦涩难懂的程度。感谢所有的回复!

于 2013-01-09T22:16:12.973 回答