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