modified libRDI.expect to reliably wake up both instruments by making sure slave is woken up first
#!/usr/bin/expect -f
#======================================================================
# L I B R D I . E X P E C T
# doc: Mon Mar 8 02:57:35 2004
# dlm: Wed Dec 29 16:00:39 2010
# (c) 2004 A.M. Thurnherr
# uE-Info: 255 0 NIL 0 0 72 2 2 8 NIL ofnI
#======================================================================
# HISTORY:
# Mar 8, 2004: - incepted (NBP0402)
# Mar 15, 2004: - released
# Mar 18, 2004: - improved download-error handling
# - BUG: set correct color in toggle_instrument
# - new reset_two_instruments
# Mar 20, 2004: - increased instrument-toggle timeout to 2s
# - cleaned-up get_deployment_name
# - added save_hardware_config to upload_file (just before CS)
# - renamed continue_logging to resume_logging
# Mar 21, 2004: - debugged user-interaction routines
# - generally increased timeouts
# Apr 4, 2004: - added comments
# Jun 15, 2004: - changed \n to \r for BB150 (CLIVAR P02)
# - started adaptation to BB150
# Jun 18, 2004: - BUG: could not handle station 008 (illegal octal digit)
# Jun 27, 2004: - doubled wait_for_startup timeouts
# Sep 28, 2006: - added sleep 1 to wait_for_startup (marked AMARYLIS)
# Oct 30, 2006: - improved error messages
# Nov 6, 2006: - added ensure_sleep
# - BUG: input_int had interpreted 010 as octal
# Nov 15, 2006: - added next_speed
# Nov 17, 2006: - added get_serial_number
# - added save_parameters
# Oct 22, 2007: - removed erroneous line of code (that did nothing)
# - changed expect target in next_speed()
# Oct 23, 2007: - cosmetics
# Nov 19, 2008: - added load_factory_defaults()
# - added print_version()
# Oct 12, 2009: - BUG: load_factory_defaults errmesg used non-existent variable
# Nov 19, 2009: - get_deployment_name needed a delay (sleep 1) for the
# instruments sent to the DIMES cruise
# Aug 25, 2010: - libBB.expect -> libRDI.expect
# Dec 1, 2010: - replaced ! by * in upload_file
# Dec 9, 2010: - increased timeout to 10s in load_factory_defaults
# Dec 10, 2010: - changed version to 1.5
# NOTE: after having written this, I am not much of a fan of TCL and and
# expect(1) syntax any more...
# PS0 ON BB150 NOTE:
# I AM NOT SURE ABOUT THE FOLLOWING ANY MORE. THE CURRENT CODE
# MAY THEREFORE NOT RUN WITH BB150s!!!
# PS0 does not produce any output on the BB150 unless the factory
# defaults are in effect. (I have not checked exactly which user defaults
# cause this behaviour.) Therefore, every time before PS0 is sent
# to the instrument, the current values are stored, the factory
# defaults restored, PS0 is sent, and then the user values
# are restored. For safety, I removed PS0 from save_hardware_config
# code for the BB150; because it would be really bad if the factory
# defaults would be restored immediately before pinging starts...
# The behaviour was detected on CPU firmware 5.52.
set NEXT_SPEED \002; # ^B # bbabble commands
set BREAK \003; # ^C
set HELP \010; # ^H
set TOGGLE \024; # ^T
set DNLOAD \030; # ^X
set cid -1; # current instrument (master or slave)
proc print_version {} {
send_user "acquire V1.5\n"
}
proc set_color {{cid -1}} { # set instrument color
if {$cid == 0} {
send_user [exec tput setaf 1];
} elseif {$cid == 1} {
send_user [exec tput setaf 4];
} else {
send_user [exec tput setaf 0];
}
}
proc error {msg} { # print error & exit
global cid;
set_color $cid;
send_error "\nError: $msg\n";
set_color;
exit 1;
}
proc gobble_prompts {} { # gobble old prompts
set continue_flushing 1;
while {$continue_flushing > 0} {
expect {
timeout {set continue_flushing 0;}
-timeout 0 ">";
}
}
}
proc BREAK {} { # send BREAK
global BREAK;
gobble_prompts;
send $BREAK; # Wakeup Instrument
expect {
timeout {error "Can't wake Instrument"}
-timeout 8 "RD Instruments (c)"
}
expect { # gobble prompt as well
timeout {error "Can't get wakeup prompt"}
-timeout 3 ">"
}
}
proc no_error_BREAK {} { # send BREAK without errcheck
global BREAK;
gobble_prompts;
send $BREAK;
expect -timeout 8 "RD Instruments (c)"
expect -timeout 3 ">"
}
proc next_speed {} { # select next baudrate
global NEXT_SPEED;
send $NEXT_SPEED;
expect {
timeout {error "Cannot change speed"}
-timeout 5 ": end"
}
sleep 1
}
proc toggle_instrument {} { # select other instrument
global cid TOGGLE;
send $TOGGLE; # switch
expect {
timeout {error "Can't switch instruments"}
-timeout 3 "Instrument"
}
if {$cid == 0} {set cid 1} else {set cid 0}
}
proc select_master {{toggle_first 1}} { # select master & check S/N
global cid master_sn slave_sn;
if {$toggle_first} toggle_instrument;
gobble_prompts;
send "\r"; wait_for_prompt; # save current value
send "PS0\r";
expect {
timeout {error "Can't determine serial number"}
-timeout 8 -re "Instrument S/N: $master_sn.*>|Xducer Ser #: $master_sn.*>|PS0...>" {}
-timeout 8 -re "Instrument S/N: $slave_sn.*>" {
toggle_instrument;
send "PS0\r";
expect {
timeout {error "Can't determine serial number"}
-timeout 8 -re "Instrument S/N: $master_sn.*>|Xducer Ser #: $master_sn.*>|PS0...>" {}
-timeout 8 -re "Instrument S/N: $slave_sn.*>" {
error "Can't switch to master"
}
}
}
}
set cid 0;
}
proc select_slave {{toggle_first 1}} { # select slave & check S/N
global cid master_sn slave_sn;
if {$toggle_first} toggle_instrument;
gobble_prompts;
send "\r"; wait_for_prompt; # save current value
send "PS0\r";
expect {
timeout {error "Can't determine master/slave"}
-timeout 8 -re "Instrument S/N: $slave_sn.*>" {}
-timeout 8 -re "Instrument S/N: $master_sn.*>|Xducer Ser #: $master_sn.*>|PS0...>" {
toggle_instrument;
send "PS0\r";
expect {
timeout {error "Can't determine master/slave"}
-timeout 8 -re "Instrument S/N: $slave_sn.*>" {}
-timeout 8 -re "Instrument S/N: $master_sn.*>|Xducer Ser #: $master_sn.*>|PS0...>" {
error "Can't switch to slave"
}
}
}
}
set cid 1;
}
proc wait_for_startup {} { # wait for bbabble startup
global cid;
expect {
timeout {error "bbabble failed to start up"}
-timeout 10 "device" {
error "No such device: cables plugged in???"
}
-timeout 10 "Instrument 0" {}
}
set cid 0;
sleep 1;
}
proc reset_instrument {} { # reset regardless of state
no_error_BREAK;
send "\r"; # send NOP command
expect {
timeout {
send_error ".";
BREAK; # try again
}
-timeout 5 ">"; # instrument response received
}
}
# In 2010 on the DIMES UK2 cruise, it was found that the instruments did
# not communicate correctly, unless the slave was woken up first. On previous
# cruises it had also been found that the order in which the instruments are
# woken up can matter, although it had not been recorded which of the instruments
# has to be woken first. An early workaround consisted in having a commented-out
# toggle_instrument statement at the beginning of reset_two_instruments. In case
# the communication did not work, that statement could simply be uncommented.
# Another workaround would be to switch the serial ports the master/slave
# ADCPs were connected to. Neither workaround is satisfacory, of course. Therefore
# I decided to modify the routine reset_two_instruments to wake up an instrument,
# determine whether it is the slave and, if not, send it back to sleep, toggle,
# and wake up the other one. I am hoping that this is solves the problems once
# and for all. If not, the code between the ### WORKAROUND markers should be
# removed because it slows down comms with the ADCPs even more. In case of
# wakeup/comms problems, the serial lines should then be switched.
proc reset_two_instruments {} { # reset regardless of state
global cid master_sn slave_sn;
set ok 0;
while {$ok < 2} {
no_error_BREAK;
send "\r"; expect {
timeout {
set ok 0;
send_error "!";
}
-timeout 5 ">" {incr ok}
}
toggle_instrument;
}
### WORKAROUND BEGIN
send "PS0\r";
expect {
timeout {error "Can't determine serial number"}
-timeout 8 -re "Instrument S/N: $master_sn.*>|Xducer Ser #: $master_sn.*>|PS0...>" {
put_to_sleep;
toggle_instrument;
put_to_sleep;
reset_two_instruments;
toggle_instrument;
}
-timeout 8 -re "Instrument S/N: $slave_sn.*>" {
toggle_instrument;
}
send "PS0\r";
expect {
timeout {error "Can't determine serial number"}
-timeout 8 -re "Instrument S/N: $master_sn.*>|Xducer Ser #: $master_sn.*>|PS0...>" {}
-timeout 8 -re "Instrument S/N: $slave_sn.*>" {
error "Can't switch to master"
}
}
}
set cid 0;
### WORKAROUND ELSE
# select_master;
### WORKAROUND END
}
proc wait_for_prompt {{tout 5}} { # wait for prompt
expect {
timeout {error "Can't get prompt"}
-timeout $tout "ERR" {
error "Instrument returned error $expect_out(buffer)";
}
-timeout $tout ">"
}
}
proc put_to_sleep {} { # send instrument to sleep
gobble_prompts;
send "\r"; wait_for_prompt;
send "CZ\r";
expect {
timeout {error "Can't put Instrument to sleep"}
-timeout 5 -re "Powering Down|POWERING DOWN"
}
}
proc save_parameters {} { # save parameters
gobble_prompts;
send "\r"; wait_for_prompt;
send "CK\r";
expect {
timeout {error "Can't put Instrument to sleep"}
-timeout 5 -re "USER defaults"
}
}
proc ensure_sleep {} { # send instrument to sleep...
gobble_prompts; # unless already sleeping
send "CZ\r";
expect {
timeout {error "Can't put Instrument to sleep"}
-timeout 5 -re "Powering Down|POWERING DOWN"
}
}
proc start_download {} { # start downloading
global DNLOAD;
gobble_prompts;
send "\r"; wait_for_prompt;
send $DNLOAD;
expect {
timeout {error "Can't start download (memory empty?)"}
-timeout 10 "Receiving:"
}
}
proc wait_for_download_finish {stn tout} { # wait for timeout to finish
global BREAK TOGGLE;
expect {
timeout {error "Download did not finish ok"}
-timeout $tout "Transfer complete" {}
-timeout $tout "ABNORMALLY" {
send $BREAK; send $TOGGLE; send $BREAK;
error "Download did not finish ok";
}
}
expect -timeout 5 ">";
global cid master_download_filename slave_download_filename;
global master_deployment_name_fmt slave_deployment_name_fmt;
if {$cid == 0} {
rename_master_download_file [format $master_deployment_name_fmt $stn]000.000;
} else {
rename_slave_download_file [format $slave_deployment_name_fmt $stn]000.000;
}
}
proc list_dir {} { # list directory
global cid;
gobble_prompts;
send "\r"; wait_for_prompt;
send "RR\r";
expect {
-timeout 1 "ERR" { # BB150
wait_for_prompt;
send "RS\r";
log_user 1;
}
-timeout 5 "Recorder Directory:";
}
set_color $cid; log_user 1;
wait_for_prompt;
log_user 0; set_color;
}
proc erase_memory {} { # erase memory
gobble_prompts;
send "\r"; wait_for_prompt;
send "RE ErAsE\r";
expect {
timeout {error "Can't erase recorder"}
-timeout 5 "Erasing..." { # BB150
sleep 3;
wait_for_prompt;
return;
}
-timeout 5 "Recorder erased." # Workhorse
}
wait_for_prompt;
}
proc save_hardware_config {filename} { # NB: logging continues in ...
gobble_prompts; # ... file given as parameter
send "\r"; wait_for_prompt;
send "TT?\r"; # BB150
expect -timeout 1 "ERR" {
wait_for_prompt;
set time [exec date +%y/%m/%d,%H:%M:%S];
set pipe [open "| tr -d \\\r \
| sed -e s@Time\\ Set.*@LOGGING\\ COMPUTER:\\ $time@ \
-e /^>/d \
> $filename" w];
log_file; log_file -a -open $pipe;
send "T?\r"; wait_for_prompt;
send "RA?\r"; wait_for_prompt;
send "RS?\r"; wait_for_prompt;
send "W?\r"; wait_for_prompt;
send "B?\r"; wait_for_prompt;
send "E?\r"; wait_for_prompt;
send "CG?\r"; wait_for_prompt;
send "CL?\r"; wait_for_prompt;
send "CP?\r"; wait_for_prompt;
send "CQ?\r"; wait_for_prompt;
send "CT?\r"; wait_for_prompt;
send "CX?\r"; wait_for_prompt;
send "PS3\r"; wait_for_prompt;
return;
}
set time [exec date +%Y/%m/%d,%H:%M:%S];
set pipe [open "| tr -d \\\r \
| sed -e s/^Press\\ any\\ key\\ to\\ continue\\ *// \
-e s@Time\\ Set.*@LOGGING\\ COMPUTER:\\ $time@ \
> $filename" w];
log_file;
log_file -a -open $pipe;
send "TT?\r"; wait_for_prompt;
send "RF\r"; wait_for_prompt;
send "RR\r"; wait_for_prompt;
send "PS0\r"; wait_for_prompt;
send "PS3\r"; wait_for_prompt;
send "AC\r"; wait_for_prompt;
send "RN?\r"; wait_for_prompt;
send "DEPLOY?\r";
expect {
timeout {error "Can't get DEPLOY? output"}
-timeout 10 "Press any key to continue"
}
send " "; wait_for_prompt;
}
proc start_logging {{filename "bbabble.log"}} { # write UNIX conformant log file
set pipe [open "| tr -d \\\r \
> $filename" w];
log_file -a -open $pipe;
}
proc resume_logging {{filename "bbabble.log"}} {# append to UNIX conf. log file
set pipe [open "| tr -d \\\r \
>> $filename" w];
log_file;
log_file -a -open $pipe;
}
proc memory_empty {} { # check whether memory is empty
gobble_prompts;
send "\r"; wait_for_prompt;
send "RA\r";
expect {
timeout {error "Can't get number of deployments"};
-timeout 3 -re {[1-9]} {
wait_for_prompt;
return 0;
}
-timeout 3 ">" {
return 1;
}
}
}
proc set_instrument_clock {} { # set instrument clock
gobble_prompts;
send "\r"; wait_for_prompt;
send "TT?\r"; # check for BB150
expect {
-timeout 1 "ERR" {
gobble_prompts;
set time [exec date +%y/%m/%d,%H:%M:%S];
send "TS$time\r";
wait_for_prompt;
return;
}
}
set time [exec date +%Y/%m/%d,%H:%M:%S];
send "TT$time\r";
wait_for_prompt;
}
proc upload_file {cfn lfn hwfn} { # upload command file
gobble_prompts; # logging stops in this routine
send "\r"; wait_for_prompt;
set cf [open $cfn "r"];
set lf [open $lfn "w"];
while {[gets $cf line] >= 0} {
regsub {[ ]*;.*} $line {} line;
if {![regexp {^[ ]*$} $line]} {
puts -nonewline $lf "$line ... ";
if {$line == "CS" || $line == "cs"} {
send_error "*";
save_hardware_config $hwfn;
send "$line\r";
expect {
timeout {};
-timeout 3 expect ">" {
error "Got prompt after CS command";
}
}
puts $lf "INSTRUMENT PINGING";
close $cf; close $lf;
log_file;
return;
}
send "$line\r";
send_error ".";
expect {
timeout {error "Can't get prompt after sending $line"};
-timeout 5 "ERR" {
error "Sending command $line returned $expect_out(buffer)";
}
-timeout 5 ">" {
puts $lf "OK";
}
}
}
}
close $cf; close $lf;
}
proc load_factory_defaults {} { # set default params
gobble_prompts;
send "\r"; wait_for_prompt;
send "CR1\r";
expect {
timeout {error "Can't get prompt"};
-timeout 10 "ERR" {
error "command CR1 returned $expect_out(buffer)";
}
-timeout 10 ">"
}
}
proc set_deployment_name {{dn UNSET}} { # set deployment name
gobble_prompts;
send "\r"; wait_for_prompt;
send "RN$dn\r";
expect -timeout 1 "ERR"; # BB150
wait_for_prompt;
}
proc get_deployment_name {} { # get deployment name
global stn_format master_deployment_name_fmt;
gobble_prompts;
send "\r"; wait_for_prompt;
send "RN?\r"; sleep 1;
expect {
timeout {error "Can't get deployment name";}
-timeout 1 "ERR" { # BB150
if {![file isfile .last_stn]} {
error "Can't find .last_stn";
}
set stn [exec cat .last_stn];
set dn [format $master_deployment_name_fmt $stn];
wait_for_prompt;
return $dn;
}
-timeout 5 -re {Current deployment name = ([^\r]*)}
}
set dn $expect_out(1,string);
wait_for_prompt;
return $dn;
}
proc get_serial_number {} { # get serial number
gobble_prompts;
send "\r"; wait_for_prompt;
send "PS0\r";
expect {
timeout {error "Can't get serial number";}
-timeout 5 -re {Instrument S/N: *([0-9]*)}
}
set sn $expect_out(1,string);
wait_for_prompt;
return $sn;
}
#======================================================================
# User-Interation Routines
#======================================================================
proc affirm {prompt {to -1} {timeout_return 0}} { # yes/no question with timeout
send_user $prompt; # and default answer
while {1} {
expect_user {
timeout {
if {$timeout_return == 0} {
send_user "N\n";
} else {
send_user "Y\n";
}
return $timeout_return;
}
-timeout $to -re "^y.*|^Y.*" {return 1;}
-timeout $to -re "^n.*|^N.*" {return 0;}
-re {..*} {send_user "Please answer Y or N..."}
}
}
}
proc input_int {prompt} { # input an integer
while {1} {
send_user $prompt;
expect_user {
-timeout -1 -re {^[0-9]+\n} {
regsub {^0*} $expect_out(0,string) {} decimal_val;
return $decimal_val;
}
-timeout -1 -re {.+} {send_user "only integers, please!\n"}
}
}
}