diff --git a/serio b/serio index 84d80c2..db8bc6b 100755 --- a/serio +++ b/serio @@ -30,6 +30,7 @@ import sys, os.path, math, time import hashlib import StringIO import uu +import datetime from getopt import GetoptError, getopt as GetOpt # verbose @@ -37,6 +38,12 @@ from getopt import GetoptError, getopt as GetOpt # 2 progress verbose = 0 +debug = 0 +def time_print(msg): + if debug: + time_str = datetime.datetime.now().strftime("%H:%M:%S.%f") + print "[%s] %s" % (time_str, msg) + # zero always will mean no checksum CHECKSUM_NONE = 0 CHECKSUM_MD5SUM = 1 @@ -110,41 +117,50 @@ class RemoteExecute: data = "" if count is not None: - for i in range(0, count): - data = self.fp.read(1) + data += self.fp.read(1) self.progress(len(data), count) elif lines is not None: - for i in range(0, lines): data += self.fp.readline() self.progress(len(data), len(data)) elif delim is not None: - while not data.endswith(delim): - data += self.serial.read(1) + data += self.fp.read(1) self.progress((len(data)-len(delim)), (len(data)-len(delim))) data = data[0:-len(delim)] - + self.progress(len(data), len(data)) if self.showprogress > 0: print return data + # read data from the serial port, for a fixed amount of time + # intended for short reads (e.g. to drain a small amount of + # data from the port, or to get the prompt) + def readuntil_time(self, duration): + saved_timeout = self.fp.timeout + self.fp.timeout = duration + data = self.fp.read(10000) + self.fp.timeout = saved_timeout + return data + def prime_before_first_execute(self): # required before first call of execute(), after this # program begins - cmd = "#" + cmd = "# pbfe" + time_print("pbfe: write:" + cmd + "\n") self.fp.write(cmd + "\n") # remote system shell echos command, slurp it up cmd_echo = self.fp.readline() + time_print("pbfe: cmd_echo=[%s]" % cmd_echo) - # The probably will not be a prompt, becuase the remote + # The probably will not be a prompt, because the remote # shell probably sent it before serio started execution. # Check for the command with carriage return, newline. # If the echoed command does not include this then @@ -152,6 +168,12 @@ class RemoteExecute: # if not cmd_echo.endswith(cmd + "\r\n"): cmd_echo = self.fp.readline() + time_print("pbfe: cmd_echo=[%s]" % cmd_echo) + + def get_prompt(self): + self.fp.write("# gp\n") + data = self.readuntil_time(1) + return data.strip() def execute(self, cmd): # This program MUST send a newline to the remote system once @@ -186,6 +208,7 @@ class RemoteExecute: if self.basic: self.execute("cat '%s' && echo '%s'" % (filename, self.DELIM)) + data = self.readuntil(delim=self.DELIM) # Catting data over a terminal turns all \n's to \r\n's @@ -256,6 +279,11 @@ class RemoteExecute: # a newline, so this does not eliminate all traffic from the # remote system, it just reduces it. self._safe_write('stty -echo\n') + # drain the response from the serial buffer + junk = self.readuntil_time(1) + + prompt = self.get_prompt() + time_print("putfile: prompt='%s'" % prompt) # Create/zero the file self._safe_write('echo -ne > %s\n' % filename) @@ -285,6 +313,10 @@ class RemoteExecute: # Show upload status self.progress(i, data_size) + # wait for shell prompt before sending next line + recv_data = self.readuntil(delim=prompt) + time_print("putfile: received data=[%s]" % recv_data) + if self.showprogress: print @@ -294,6 +326,7 @@ class RemoteExecute: return i def _safe_write(self, data): + time_print("_safe_write:"+data) self.fp.write(data) if data.endswith('\n'): # give the remote system time for disk/flash I/O @@ -317,7 +350,6 @@ class RemoteExecute: # should do this in a thread so we can output while # the data is coming back - # FIXME - use timeout here result = self.readuntil(None, None, self.DELIM) if remote_shell: @@ -335,10 +367,10 @@ class RemoteExecute: class SerialTerminal(RemoteExecute): - def __init__(self, basic, checksum, port, baudrate, io_time): + def __init__(self, basic, checksum, port, baudrate, io_time, timeout): self.basic = basic self.checksum = checksum - self.serial = serial.Serial(port=port, baudrate=baudrate) + self.serial = serial.Serial(port=port, baudrate=baudrate, timeout=timeout) already_open = self.serial.isOpen() if not already_open: self.serial.open() @@ -411,6 +443,7 @@ def main(): checksum = CHECKSUM_NONE destination = None io_time = None + timeout = None minicom = None paranoid = None port = '/dev/ttyUSB0' @@ -419,7 +452,7 @@ def main(): try: opts, args = GetOpt(sys.argv[1:], - 'b:Bc:d:ghMNm:pPs:t:vy:', + 'b:Bc:d:ghMNm:pPs:t:T:vy:', ['baudrate=', 'basic', 'cmd=', @@ -433,6 +466,7 @@ def main(): 'no-remote-shell', 'source=', 'time=', + 'timeout=', 'verbose', 'port=', ] @@ -471,6 +505,8 @@ def main(): source = arg elif opt in ('-t', '--time'): io_time = float(arg) + elif opt in ('-T', '--timeout'): + timeout = float(arg) elif opt in ('-v', '--verbose'): verbose += 1 elif opt in ('-y', '--port'): @@ -499,7 +535,7 @@ def main(): port = minicom.port() baudrate = minicom.baudrate() - sterm = SerialTerminal(basic, checksum, port, baudrate, io_time) + sterm = SerialTerminal(basic, checksum, port, baudrate, io_time, timeout) if action == 'put': try: diff --git a/serio-test.sh b/serio-test.sh index f7c8278..da83e5b 100755 --- a/serio-test.sh +++ b/serio-test.sh @@ -17,14 +17,15 @@ echo " usage: ${script_name} [-v] [--debug] [-h] [-nc] -B, --basic *Use basic (minimal) remote system commands - --debug Print each shell line as it is executed - -F, --numfile Number of random data test files + --debug Print each shell line as it is executed + -F, --numfiles Number of random data test files -h, -help, --help Show help -nc Don't clean up test files -s, --sleep Time to sleep between --get and --put tests [0.0] -t, --time *Delay in _safe_write() + -T, --timeout *Timeout for serial port I/O operations -y, --port *Serial port to use [/dev/ttyUSB0] - --remote-dir Directory on remote system to put files into [.] + -R, --remote-dir Directory on remote system to put files into [.] -v Be verbose * Sets a serio command line option @@ -44,10 +45,11 @@ unset SERIAL_DEV unset PARANOID unset SLEEP unset TIME +unset TIMEOUT do_cleanup=1 help=0 -numfile=1 +numfiles=2 remote_dir="." verbose=0 while [ $# -gt 0 ] ; do @@ -64,9 +66,9 @@ while [ $# -gt 0 ] ; do set -x ;; - -F | --numfile ) + -F | --numfiles ) shift - numfile=$1 + numfiles=$1 shift ;; @@ -98,7 +100,14 @@ while [ $# -gt 0 ] ; do verbose=1 ;; - -T | --remote-dir ) + -T | --timeout ) + shift + TIMEOUT="-T $1" + shift + verbose=1 + ;; + + -R | --remote-dir ) shift remote_dir="$1" shift @@ -148,13 +157,13 @@ fi tmp_dir=$( mktemp --tmpdir -d serio-test.XXXXXXXXXX ) -FILE_LIST="file_1" -for k in $( seq 2 $(( ${numfile} + 1 )) ) ; do +FILE_LIST="" +for k in $(seq ${numfiles}) ; do FILE_LIST="${FILE_LIST} file_${k}" done -SERIO_ARGS="$SERIAL_DEV $PARANOID $BASIC $TIME" +SERIO_ARGS="$SERIAL_DEV $PARANOID $BASIC $TIME $TIMEOUT" # shell builtin 'time' does not recognize -f TIME="/usr/bin/time" @@ -177,7 +186,7 @@ vecho "SERIO = ${SERIO}" echo "Creating files..." # make a super-small, super-safe file for file1 -echo "this is the first test file for serio" >file1 +echo "this is the first test file for serio" >${tmp_dir}/file_short if [ $verbose -gt 0 ] ; then ddout="/dev/stdout" @@ -194,6 +203,9 @@ for f in ${FILE_LIST} ; do i=$(( $i + 1 )) done +FILE_LIST="file_short ${FILE_LIST}" + + ######################### # test put and get # run some put and get tests, timing them @@ -276,18 +288,37 @@ vecho " Execute 'ls -l $remote_dir'" $SERIO $SERIO_ARGS -c "ls -l $remote_dir" vecho " Execute 'echo hello there'" -res1=$($SERIO $SERIO_ARGS -c "echo hello there") -exp1=$'hello there' +res=$($SERIO $SERIO_ARGS -c "echo hello there") +rcode=$? +exp=$'hello there' -echo "expected : [$exp1]" -echo "got result: [$res1]" +echo "expected : [$exp], rcode=[0]" +echo "got result: [$res], rcode=[$rcode]" desc="$test_num - run 'echo hello there' on remote" -if [ "$res1" = "$exp1" ] ; then +if [ "$res" = "$exp" -a "$rcode" = "0" ] ; then + echo "ok $desc" +else + echo "not ok $desc" +fi +test_num=$(( $test_num + 1 )) + +vecho " Execute 'echo foo ; false'" +res=$($SERIO $SERIO_ARGS -c "echo foo ; false") +rcode=$? +exp=$'foo' +expcode=1 + +echo "expected : [$exp], rcode=[$expcode]" +echo "got result: [$res], rcode=[$rcode]" + +desc="$test_num - run 'echo foo ; false' on remote" +if [ "$res" = "$exp" -a "$rcode" = "$expcode" ] ; then echo "ok $desc" else echo "not ok $desc" fi +test_num=$(( $test_num + 1 )) ######################### # test cleanup @@ -295,7 +326,7 @@ fi function cleanup { # remove test files - $SERIO $SERIAL_DEV -c "rm ${remote_dir}/file_[1-9]*" + $SERIO $SERIAL_DEV -c "rm ${remote_dir}/file_[1-9]* ${remote_dir}/file_short" rm -r ${tmp_dir} }