处理命令时停止冻结TCL脚本

时间:2013-01-09 20:26:12

标签: tcl progress visa

好的,我在高层做的是扫描系统中所有连接到它的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方面阻止这种情况,因为我无法知道我将要查询的设备类型。我尝试过使用"更新"和"更新idletasks"在剧本的几个不同的地方,但它只是让我在冻结之间的片刻。

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

3 个答案:

答案 0 :(得分: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,您可以使用协同程序使您的过程作为一个合作的“后台线程”工作:它很容易实现“获取超时”,它从协程生成并在完成或超时后重新进入。但是,还没有现成的解决方案。

答案 1 :(得分:2)

通过将I / O通道设置为非阻塞并使用fileeventchan event来执行此操作以使用tcl的事件循环的标准方法;但是,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需要以不同的方式完成),但这显示了基本方法。

答案 2 :(得分:1)

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

fconfigure $TemporaryChannel -timeout 100

设置此超时并不能完全解决问题,但会将其降低到默默无闻的程度。感谢所有回复!