在处理命令时阻止TCL脚本冻结

在处理命令时阻止TCL脚本冻结,tcl,progress,visa,Tcl,Progress,Visa,好的,我在高层所做的是扫描一个系统,寻找所有连接到它的VISA设备,并让它们识别自己 问题在于,并非所有VISA设备都支持识别自身身份的功能,而我所知道的唯一方法就是告诉设备这样做。这迫使那些无法识别自己身份的人依赖至少1秒的超时。在等待超时时,我的TCL脚本和希望的应用程序将冻结,直到超时完成。对于多个设备,这会让我有一个尴尬的等待时间,可能长达几秒钟,我无法向用户更新正在发生的事情 这是我的密码: proc ::VISA::Scan {} { # Open a temporary r

好的,我在高层所做的是扫描一个系统,寻找所有连接到它的VISA设备,并让它们识别自己

问题在于,并非所有VISA设备都支持识别自身身份的功能,而我所知道的唯一方法就是告诉设备这样做。这迫使那些无法识别自己身份的人依赖至少1秒的超时。在等待超时时,我的TCL脚本和希望的应用程序将冻结,直到超时完成。对于多个设备,这会让我有一个尴尬的等待时间,可能长达几秒钟,我无法向用户更新正在发生的事情

这是我的密码:

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”和“updateidletasks”,但它只是给了我一个暂停的时间


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

必须使用
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]
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]
使通道非阻塞,安装fileevent处理程序:

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,您可以使用协同程序使您的过程作为一个协作的“后台线程”工作:很容易实现“带超时的gets”,它从协同程序生成,并在完成或超时时重新输入。不过,还没有现成的解决方案。

您必须在
之后使用
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]
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]
使通道非阻塞,安装fileevent处理程序:

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,您可以使用协同程序使您的过程作为一个协作的“后台线程”工作:很容易实现“带超时的gets”,它从协同程序生成,并在完成或超时时重新输入。不过,目前还没有现成的解决方案。

标准方法是通过将I/O通道设置为非阻塞并使用
fileevent
chan event
来使用tcl的事件循环;但是,tclvisa文档说明visa频道不支持fileevent

因此,下一个最好的方法是使用非阻塞I/O(它只是将超时设置为0)和BusyLop读取通道或在延迟后读取通道;其中任何一个都应该通过事件循环来处理,而不是通过在周围喷洒
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"
这会持续重新安排
读取
,只要它一直返回空值


这将需要重新构造一点,以便在更大的gui程序中工作(vwait和超时需要以不同的方式完成),但这显示了基本方法。

标准方法是通过将I/O通道设置为非阻塞并使用
fileevent
chan event
来使用tcl的事件循环;但是,tclvisa文档说明visa频道不支持fileevent

因此,下一个最好的方法是使用非阻塞I/O(它只是将超时设置为0)和BusyLop读取通道或在延迟后读取通道;其中任何一个都应该通过事件循环来处理,而不是通过在周围喷洒
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"
这会持续重新安排
读取
,只要它一直返回空值


这需要重新构造一点,以便在更大的gui程序中工作(vwait和超时需要以不同的方式完成),但这显示了基本方法。

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

fconfigure $TemporaryChannel -timeout 100

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

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

fconfigure $TemporaryChannel -timeout 100

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

@evilotto指出(正如我以前不知道的那样),我正在使用的包tclvisa不支持其channelsWell上的fileevent命令,您可以使用线程(如果
tclvisa
东西与线程构建兼容)。在TCL中,线程在单独的解释器中运行,使用
thread::send
进行通信(以及其他一些方式)。。。我对TCL还比较陌生,但我参考的那本书说,在TCL线程中使用扩展是不安全的,而tclvisa是一个C扩展。任何想法,