好的,我在高层做的是扫描系统中所有连接到它的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"在剧本的几个不同的地方,但它只是让我在冻结之间的片刻。
任何帮助将不胜感激。提前谢谢!
答案 0 :(得分: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]
使频道无阻塞,安装文件事件处理程序:
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通道设置为非阻塞并使用fileevent
或chan 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
设置此超时并不能完全解决问题,但会将其降低到默默无闻的程度。感谢所有回复!