[plt-scheme] Need low-level help: blocking on reading from serial port in Windows

From: Nadeem Abdul Hamid (nadeem at acm.org)
Date: Fri Oct 23 16:04:26 EDT 2009

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
>


Posted on the users mailing list.