#!/usr/local/bin/kermit +
#
# m o d e m t e s t 2
#
# Modem test and statistics -- Works on Windows, UNIX, and VMS.
#
# Requires C-Kermit 7.1
# F. da Cruz, Columbia University, Feb 2001
#
# Sometimes it's easier to hardwire the parameters...
#
.device = com1
.modem = usr
.speed = 115200             ; For USR v.34 use 57600 and for V.90 use 115200
.origin = 212-316-3982
.\%1 = watsun
.\%2 = fdc
.\%3 = test.zip

; Note: phone numbers are fake

dcl \&n[] = 5551234 5551235 5551236 5551237 5551238 5551239 5551240 -
            5551241 5551242 5551243 5551244 5551245 5551246 5551247

; Array of terminal server names
dcl \&p[] = ccts1 ccts2 ccts3 ccts4 ccts5 ccts6

.\%n := \fdimension(&n)             ; Number of numbers

; Array of terminal server prompts (= name with ">" appended)
dcl \&q[\fdim(&p)]                  
for \%i 1 \fdim(&p) 1 {
  .\&q[\%i] := \&p[\%i]>
}

; This script takes great care to recover from errors so it can run
; unattended for long periods of time and to record whatever data it got
; on each run even if the session terminated prematurely.
;
; As written, this script assumes a Cisco terminal server that has a
; "terminal autohangup" command.  SET CARRIER-WATCH is OFF at first, so we
; can query and then dial the modem.  After successful dialing, we set it
; ON so we can catch disconnections promptly.  We also send "terminal
; autohangup" to the Cisco so when the host session terminates (as it
; should when we send it a BYE command), the terminal server hangs up the
; modem.  All i/o's are tested for success or failure.  Once the data
; transfer phase is complete, we SET CARRIER-WATCH OFF again so we can
; query the modem for stats.
;
; Modem queries are supported for US Robotics and Compaq modems, and
; tested with USR V.34 and "V.Everything" 56K, and Compaq Presario 56K-DF.
;
define badversion echo C-Kermit 7.1 or K95 1.1.21 or later required, exit
if LLT \v(version) 701199 badversion
if not def \v(buildid) badversion
if LLT \v(buildid) 20001116 badversion

; local model firmware \%i \%j
; local debug retrains blers ispeed ospeed
; local user pass host file upcps dncps head tsport seq

; Configuration parameters -- change as needed (*)

;; LOG SESSION session.log append        ; (*) uncomment for extra debugging

SET DIAL TIMEOUT 30
SET SEND TIMEOUT 6 FIXED
SET INPUT BUFFER-LENGTH 10240

.debug = 1                               ; (*) Change to 0 for minimal messages
.logfile = modem.log                     ; (*) Log file name

switch \v(system) {                      ; Device and modem defaults...
  :win32                                 ; For Windows 95/98/NT/2000
    if not def device .device = tapi     ; (*) Change if necessary
    if not def modem .modem = tapi       ; (*) Ditto
    if not def speed .speed = 115200     ; (*) Port speed - change if necessary
    break
  :unix                                  ; For UNIX...
    if not def device .device = /dev/cua ; (*) Change to your dialout device
    if not def modem .modem = usrobotics ; (*) Change to match your modem
    if not def speed .speed = 115200     ; (*) Port speed - change if necessary
    break
  :vms                                   ; For VMS...
    if not def device .device = TTA0:    ; (*) Change to your dialout device
    if not def modem .modem = usrobotics ; (*) Change to match your modem
    if not def speed .speed = 57600      ; (*) Port speed - change if necessary
    break
  :default
    ; Add others here...
    exit 1 Sorry - this script does not run on \v(platform).
}

if not def origin .origin = UNKNOWN
.model = UNKNOWN                    ; Modem model
.firmware = UNKNOWN                 ; Modem firmware

if def \%1 .host := \%1             ; Accept host, user, and filename
if def \%2 .user := \%2             ; from command line (UNIX only)
if def \%3 .file := \%3             ; or from TAKE command line.

; Macro definitions

def usrinfo {                       ; Get info about USR modem
    ; If we weren't cheating we'd do ATI6 and parse the results.
    ; See usrgetstats for a non-cheating example.
    ; Meanwhile change the strings to match your own modem.
    if eq "\v(speed)" "115200" {
        .model = USRobotics Courier V.Everything V.90
        .firmware = 7.3.14 / 3.0.13
    } else {
        .model = USRobotics Courier V.32bis V.34+ Fax
        .firmware = 6.2.3 / 1.2.4
    }
}

def cpqinfo {                       ; Get info about Compaq modem
    ; If we weren't cheating we'd do ATI and parse the results.
    ; See cpqgetstats for a non-cheating example.
    ; Meanwhile change the strings to match your own modem.
    .model = Compaq Presario 56K-DF
    .firmware = 1.3.12
}

def getmdminfo {                    ; Get modem info.
    switch \v(modem) {
      :usrobotics
         usrinfo, break
      :tapi
      :compaq
         cpqinfo, break
    }
}

; Get and parse a statistics report from a USR modem.
; This version parses the report on the fly.

def usrstats {                      ; Get USR connection statistics.
    local \%x \%y \%z
    clear input
    output ATI6\13                  ; Send ATI6 and parse output.
    set flag off
    set input echo on
    while not flag {
       minput 10  Blers  Retrains  Speed  OK
       switch \v(minput) {
	 :1, clear input, input 2 \10, .blers := \fword(\v(input),1), break
	 :2, clear input, input 2 \10, .retrains := \fword(\v(input),5), break
	 :3, clear input, input 2 \10, .ospeed := \fword(\v(input),1,,/), break
	 :4, set flag on, break
       }
    }
}

; Get and parse a statistics report from a USR modem.
; This version (which works but is not used) reads the whole report at
; once and parses it afterwards.  The Compaq report parser (next macro)
; also uses this method.

def xusrstats {                     ; Get USR connection statistics.
    local \%x \%y \%z
    clear input
    output ATI6\13                  ; Send ATI6 and parse output.
    input 20 OK		  
    if fail end 1
    .\%x := \findex(Retrains Granted,\v(input))
    if \%x .retrains := \fword(\fsubstr(\v(input),\%x),3)
    .\%x := \findex(Blers,\v(input),\%x+16)
    if \%x .blers := \fword(\fsubstr(\v(input),\%x),2)
    .\%x := \findex(Speed,\v(input),\%x+16)
    if \%x .ospeed := \fword(\fsubstr(\v(input),\%x),2,,/)
}

; Get and parse a statistics report from a Compaq modem.
; This version reads the whole report at once and parses it afterwards.

def cpqstats {                      ; Get Compaq connection statistics.
    local \%x \%y \%z
    clear input
    output ATI11\13                 ; Send ATI11 and parse output.
    if fail end 1
    input 6 ESC to quit.
    if fail end 1
    output {\32}                    ; Tickle "press any key to continue"
    minput 20 OK {NO CARRIER}
    if fail end 1
    .\%x := \findex(Final   Transmit Carrier Rate,\v(input))
    if \%x .\%y := \fword(\fsubstr(\v(input),\%x),5)
    .\%x := \findex(Final   Receive  Carrier Rate,\v(input))
    if \%x .\%z := \fword(\fsubstr(\v(input),\%x),5)
    if \%x .ospeed := \%y/\%z    
    .\%x := \findex(Receive  Frame Error Count,\v(input))
    if \%x .blers := \fword(\fsubstr(\v(input),\%x),5)
    .\%x := \findex(Retrain by Local  Modem,\v(input))
    if \%x .retrains := \fword(\fsubstr(\v(input),\%x),5)
}
def getmdmstats {                   ; Get modem connection statistics.
    switch \v(modem) {
      :usrobotics
         usrstats, break
      :tapi
      :compaq
         cpqstats, break
    }
}
def openlog {                       ; Open the log file and write heading
    fopen /append \%c \m(logfile)
    if fail exit 1 Can't open log "\m(logfile)"
    fwrite \%c
    fwrite \%c {Time:            \v(date) \v(time)}
    fwrite \%c {Platform:        \v(platform)}
    fwrite \%c {Software:        \v(xprogram) \v(xversion)}
    fwrite \%c {Modem:           \m(model)}
    fwrite \%c {Firmware:        \m(firmware)}
    fwrite \%c {Interface Speed: \m(speed)}
    fwrite \%c {Calling from:    \m(origin)}
    fwrite \%c {Log File:        \m(logfile)}
    fwrite \%c {Test File:       \m(file)}
    fwrite \%c {Test File Size:  \fsize(\m(file))}
    fwrite \%c
    fwrite /string \%c {Date  Time  Number    Line       Speed  Speed-After  }
    fwrite \%c {Blers Retr Upload  Dnload}
    fwrite /string \%c {----------  -------   --------   -----  -----------  }
    fwrite \%c {----- ---- ------  ------}
    fclose \%c
}

define logrecord {                   ; Write a log entry
    if \f_status(\%c) fclose \%c
    fopen /append \%c \m(logfile)
    if fail exit 1 LOG OPEN FAILURE: "\m(logfile)"
    fwrite \%c {\m(head)  \%1}
    if fail exit 1 LOG WRITE FAILURE: "\m(logfile)"
    fclose \%c
    if \m(debug) { echo, echo {\m(head)  \%1} }
}

define dologin {                     ; Login from terminal server to host
    output telnet \m(host)\13        ; and start Kermit in server mode there.
    input 40 login:
    if fail end 1
    out \m(user)\13
    input 20 Password:
    if fail end 1    
    out \m(pass)\13
    input 90 {$ }
    output kermit -x\13
    input 60 READY TO SERVE...
    if fail end 1
}

def xmsg {                           ; Send message to file transfer display
  if not def \v(buildid) end
  if llt \v(buildid) 20001116 end
  set xfer message \m(seq): \v(dialnumber) (\m(tsport)) \v(time)
}

def doio {                           ; Transfer a file back & forth 
    local rc                         ; and get performance statistics
    .rc = 1
    while exist \m(file) {
        remote cd /tmp
        if fail break
        remote set file collision overwrite
        do xmsg
	send \m(file) delete.me
	if fail break
	.upcps := \v(cps)
        do xmsg
	get delete.me
        if fail break
	.dncps := \v(cps)
        .rc = 0
        break
    }
    if not open connection end 1
    bye
    end \m(rc)
}

; End of macro definitions

while not def host {                 ; Get hostname 
    ask host { Host: }               ; if not given on command line...
}
while not def user {                 ; Ditto for username...
    ask user { User: }
}
while not def pass {                 ; Always prompt for password.
    askq pass { Password: }
}
while not def file {                 ; Get filename if not given 
    ask file { File to transfer: }   ; on command line.
    if exist \m(file) break
    echo \m(file) not found.
}
if not equal "\m(device)" "tapi" {   ; Except in K95...
    set modem type \m(modem)         ; Specify modem type
    if fail {                        ; Check for error
        exit 1 {Error: Modem type "\m(modem)" not known}
    }
}
set carrier off                      ; Don't require carrier yet
set line \m(device)                  ; Open the device
if fail {                            ; Check for failure
    exit 1 {Error: \m(device) not available}
}
set speed \m(speed)                  ; Set the desired speed

getmdminfo                           ; Get modem info
set xfer bell off                    ; Turn off annoying bell

if \m(debug) {                       ; Echo parameters for this run...
    echo { Device:      \m(device)}
    echo { Speed:       \m(speed)}
    echo { Modem:       \m(modem)}
    echo { Model:       \m(model)}
    echo { Firmware:    \m(firmware)}
    echo { Host:        \m(host)}
    echo { User:        \m(user)}
    echo { Password:    \frepeat(*,\flen(\m(pass)))}
    echo { File:        \m(file)}
    echo { Size:        \fsize(\m(file))}
    set modem speaker on
    set dial display on
    set input echo on
    set xfer display full
} else {
    set modem speaker off
    set dial display off
    set input echo off
    set quiet on
    set xfer display brief
}

; Device is open...

set dial retries 0                   ; No redialing
set dial speed-matching off          ; No speed changing
def on_exit hangup                   ; In case of Ctrl-C
set exit warning off                 ; In case of misconfigured modem
set file collision overwrite         ; Don't pile up lots of backup files
set bell off                         ; Silence please
if \m(debug) show comm

openlog                              ; Start logging
.seq = 0                             ; Call number

while true {                         ; Loop till user types something
    for \%i 1 \%n 1 {                ; Call each phone number
        incr seq                     ; Count this call
        .status = ERRORS             ; Assume there will be errors (for msg)
        set carrier off              ; For dialing
        .\%9 := \fcvtdate(\v(date) \v(time))
        .date := \fsubstr(\%9,5,2)/\fsubstr(\%9,7,2)
        .time := \fsubstr(\%9,10,2)\fsubstr(\%9,13,2)
        .head := \m(date) \m(time)  \&n[\%i]
        clear dial-status
        dial \&n[\%i]
        if fail {
            switch \v(dialstatus) {
              :8, logrecord {FAILED: Timed out}, break
              :9, logrecord {FAILED: User canceled}, stop
              :10, logrecord {FAILED: Modem not ready}, break
              :default, logrecord {FAILED: "\v(dialresult)"}, break
            }
            continue
        }
	set carrier on               ; For logging in and file transfer
        .ispeed := \fword(\v(dialresult),2) ; Speed from CONNECT message

        clear input
	.\%x := 0    
        for \%j 1 10 1 {             ; Get terminal server herald
            output \13
            xecho {#}
            minput 10 \fjoin(&q,,2)
            if success break
        }
        if > \%j 10 {
            logrecord {FAILED: No terminal server prompt}
	    continue
        }
	.tmp := \v(input)
        .\%x = \v(minput)
	.\%y = \findex({\&p[\%x] line },\m(tmp))
        .tsport = UNK
	if > \%y 0 {
	  .tsport := \flpad(\fword(\s(tmp[\%y]),3),3,0)
	}
	.server := \&p[\%x]
        .tsport := \m(server):\m(tsport)
        .head := \m(head)\flpad(\m(tsport),12)
        .upcps = FAILED
        .dncps = FAILED
        lineout term autohangup
        minput 6 \fjoin(&q,,2)
        if success { 
            dologin
            if success doio            
            if success .status = OK
        }
        pause 1
	set carrier off              ; For getting modem statistics
        hangup                       ; Hang up modem but don't close
        pause 1
        .retrains = UNK
        .blers = UNK
        .ospeed = UNKNOWN
        output AT\13                 ; Make sure we're in command mode.
        input 3 OK
	if success getmdmstats       ; We are - Get statistics from modem.
	.\%7 := \flpad(\m(ispeed),5)\flpad(\m(ospeed),13)
	.\%8 := \flpad(\m(blers),6) \flpad(\m(retrains),4)
	.\%9 := \flpad(\m(upcps),6) \flpad(\m(dncps),7)
	logrec {\%7 \%8 \%9}         ; Write log record
	set ask-timer 5
        echo
        echo     {----------------------------------------}
        echo PASS \m(seq) DONE (\m(status))...
        clear keyboard-buffer
        getc \%j {PRESS ANY KEY WITHIN 5 SECONDS TO QUIT: }
        echo     {----------------------------------------}
        if not asktimeout stop
        set ask-timer 0
        echo
        echo Continuing...
    }
}
exit
