[plt-scheme] Need low-level help: blocking on reading from serial port in Windows
Thanks Carl for the suggestion. I've tried read-bytes-avail!* but
still have a similar issue. Here's my function now:
;; raw-read : number bot -> (listof byte)
(define (raw-read n a-bot)
;(printf " raw-read")
(local [(define bstr (make-bytes n))
(define in (bot-in-port a-bot))
;; read-aux : number port -> number
(define (read-aux start-pos tries)
(when (= 0 (remainder tries 10000000)) (sleep 0)) ; (printf "."))
(cond [(>= start-pos n) start-pos] ; should never be > really
[else
(let [(r (read-bytes-avail!* bstr in start-pos))]
(cond [(number? r) (read-aux (+ r start-pos) 0)]
[else (read-aux start-pos (add1 tries))] ; ???
))]))
]
(read-aux 0 0)
(bytes->list bstr)
))
Notice the "when" expression. If that is commented out, the code still
hangs... Here's my interactions (apologies for the length of the run
below; I wasn't sure if I attached it as a file that it would go
through to the list):
With (sleep 0) in when expression runs reliably (results produced
within a second; (mostly) no hanging):
Welcome to DrScheme, version 4.2.1 [3m].
Language: Module; memory limit: 128 megabytes.
All 8 tests passed!
> (with-bot-on-port
"COM3"
(lambda (b)
(for ([i (in-range 10)])
(printf "~a\n" (get-senses b)))))
info fluke:2.7.9,Robot-Version:2.6.1,Robot:Scribbler,Mode:Serial
battery 6.386639531392239
name DrWeird
senses #(struct:sensor-data 1 1 150 79 257 1 0 0)
#(struct:sensor-data 1 1 150 79 256 1 0 0)
#(struct:sensor-data 1 1 150 79 256 1 0 0)
#(struct:sensor-data 1 1 150 79 256 1 0 0)
#(struct:sensor-data 1 1 150 79 256 1 0 0)
#(struct:sensor-data 0 1 150 79 256 1 0 0)
#(struct:sensor-data 1 1 150 79 256 1 0 0)
#(struct:sensor-data 1 1 150 79 256 1 0 0)
#(struct:sensor-data 0 1 150 79 256 1 0 0)
#(struct:sensor-data 1 1 150 79 256 1 0 0)
#(struct:sensor-data 0 1 150 79 256 1 0 0)
battery 6.386639531392239
senses #(struct:sensor-data 1 1 150 79 256 1 0 0)
> (with-bot-on-port
"COM3"
(lambda (b)
(for ([i (in-range 10)])
(printf "~a\n" (get-senses b)))))
info fluke:2.7.9,Robot-Version:2.6.1,Robot:Scribbler,Mode:Serial
battery 6.386639531392239
name DrWeird
senses #(struct:sensor-data 0 1 137 74 231 1 0 0)
#(struct:sensor-data 1 1 137 74 231 1 0 0)
#(struct:sensor-data 1 1 137 74 232 1 0 0)
#(struct:sensor-data 0 1 137 74 232 1 0 0)
#(struct:sensor-data 1 1 137 74 232 1 0 0)
#(struct:sensor-data 1 1 137 74 233 1 0 0)
#(struct:sensor-data 0 1 138 75 233 1 0 0)
#(struct:sensor-data 1 1 138 75 234 1 0 0)
#(struct:sensor-data 1 1 138 75 235 1 0 0)
#(struct:sensor-data 0 1 139 75 235 1 0 0)
#(struct:sensor-data 1 0 139 75 236 1 0 0)
battery 6.43430102043248
senses #(struct:sensor-data 1 1 141 76 237 1 0 0)
>
With the entire when commented out (hangs at different points):
> (with-bot-on-port
"COM3"
(lambda (b)
(for ([i (in-range 10)])
(printf "~a\n" (get-senses b)))))
info fluke:2.7.9,Robot-Version:2.6.1,Robot:Scribbler,Mode:Serial
battery 6.386639531392239
;;;;;;;;;;;;;;;;;;;; HANG -- no response even after 30 seconds
With (printf "."), works sometimes, but then hangs sometimes:
Welcome to DrScheme, version 4.2.1 [3m].
Language: Module; memory limit: 128 megabytes.
All 8 tests passed!
> (with-bot-on-port
"COM3"
(lambda (b)
(for ([i (in-range 3)])
(printf "~a\n" (get-senses b)))))
.................... info
fluke:2.7.9,Robot-Version:2.6.1,Robot:Scribbler,Mode:Serial
...battery 6.243655064271517
...................................... name DrWeird
...................... senses #(struct:sensor-data 0 0 188 88 277 1 1 0)
......................#(struct:sensor-data 1 0 188 88 277 1 1 0)
......................#(struct:sensor-data 1 1 189 88 278 1 1 0)
......................#(struct:sensor-data 1 1 189 88 277 1 1 0)
.........................battery 6.195993575231277
...................... senses #(struct:sensor-data 1 1 189 88 277 1 1 0)
> (with-bot-on-port
"COM3"
(lambda (b)
(for ([i (in-range 3)])
(printf "~a\n" (get-senses b)))))
.................... info
fluke:2.7.9,Robot-Version:2.6.1,Robot:Scribbler,Mode:Serial
...battery 6.291316553311758
...................................... name DrWeird
...................... senses #(struct:sensor-data 1 1 187 88 277 1 1 0)
......................#(struct:sensor-data 1 1 187 88 277 1 1 0)
......................#(struct:sensor-data 0 0 187 88 277 1 1 0)
......................#(struct:sensor-data 1 0 187 88 277 1 1 0)
.........................battery 6.243655064271517
...................... senses #(struct:sensor-data 1 0 187 88 277 1 1 0)
> (with-bot-on-port
"COM3"
(lambda (b)
(for ([i (in-range 3)])
(printf "~a\n" (get-senses b)))))
.
;;;;;; HANG!!!
Anyway, I'm going to try with the (sleep 0) option and see how that
works for now. I'm trying to wrap the robot functionality up so they
can be programmed using big-bang... Will post details of that in reply
to Dave's question....
--- nadeem
On Thu, Oct 22, 2009 at 6:31 PM, Carl Eastlund <carl.eastlund at gmail.com> wrote:
> On Thu, Oct 22, 2009 at 6:10 PM, Nadeem Abdul Hamid <nadeem at acm.org> wrote:
>> Hello all,
>>
>> I'm working on a Scheme interface to some robots and am having issues with
>> reading from a serial port (really a wireless Bluetooth connection) on
>> Windows. Basically, I need help writing a function that efficiently reads
>> data from the port (without running into deadlock apparently). Here's the
>> function in question; it's supposed to read a specified number of bytes from
>> the input port in a "bot" structure and return them in a list:
>>
>> ;; raw-read : number bot -> (listof byte)
>> (define (raw-read n a-bot)
>> ;(printf " raw-read")
>> (local (;; read-aux : (listof byte) number port -> (listof byte)
>> (define (read-aux acc n in)
>> ;(sleep (/ LATENCY 1000))
>> ;(printf ".~a" acc)
>> (cond
>> [(= n 0) acc]
>> [else
>> (cond
>> [(byte-ready? in) (read-aux (cons (read-byte in) acc)
>> (- n 1) in)]
>> [else (read-aux acc n in)])]))
>> (define r (reverse (read-aux empty n (bot-in-port a-bot)))))
>> ;(printf " done: ~a\n" r)
>> r
>> ))
>>
>> First, everything works fine beautifully on Mac OS X. Then I went to try
>> this on Windows XP (using PLT Scheme 4.2.2) and it would hang in this
>> function. I've finally figured out that if I uncomment the "sleep"
>> statement, then it does *not* hang. However, that makes performance very
>> sluggish (even though LATENCY is defined as .01). So I'm wondering if
>> without the sleep there is some sort of deadlock happening with the
>> byte-ready? function? I suppose I could keep a counter and only "sleep"
>> every 100 or 1000 failures of the byte-ready? condition, but does anyone
>> have suggestions for a better way to do this? To tell the truth, I don't
>> remember why I used byte-ready? to begin with -- in the morning I'll try
>> without it and see what happens by just letting "read-byte" block until
>> there's data.
>>
>> The port in the "bot" structure was opened using:
>> (open-input-output-file portname #:mode 'binary #:exists 'update)
>> where on windows portname is something like "\\\\.\\COM10".
>>
>>
>> Thanks in advance for suggestions, comments. I'm still a Scheme newbie so
>> welcome any criticisms :-)
>>
>> --- nadeem
>
> Nadeem,
>
> You may want to look at the read-bytes-avail* function:
>
> http://docs.plt-scheme.org/reference/Byte_and_String_Input.html#(def._((quote._~23~25kernel)._read-bytes-avail!*))
>
> It reads a byte string from a port and returns it immediately, never
> blocking. It looks like it does the work for you.
>
> --Carl
>