1 ; File: "robot.scm", Time-stamp: <2006-03-01 15:57:44 feeley>
3 ; Copyright (C) 2006 by Marc Feeley, All Rights Reserved.
5 ; usage: usage: robot [[BASE_HOSTNAME|.] ID [HEX_FILE]]
9 ;------------------------------------------------------------------------------
11 (define default-base "localhost") ; internet address of base-station server
12 (define port-number 12345)
14 ;------------------------------------------------------------------------------
16 (define version-addr 6)
17 (define program-filename "robot.hex")
18 (define program-start-addr #x2000)
20 (define serial-port-name "com1") ; default, works for Windows
21 (define serial-port-name "rs232") ; ADDED now to named pipe
23 (let loop ((lst '("/dev/cu.USA28X181P1.1"
24 "/dev/cu.USA28X181P2.2"
25 "/dev/cu.USA28X191P1.1"
26 "/dev/cu.USA28X191P2.2"
30 (let ((name (car lst)))
31 (if (file-exists? name)
32 (set! serial-port-name name)
35 ;------------------------------------------------------------------------------
39 (with-exception-catcher
43 (open-output-file (list path: "robot.log" buffering: 'line))))))
45 ;------------------------------------------------------------------------------
47 (current-user-interrupt-handler exit)
52 (display "usage: robot [[BASE_HOSTNAME|.] ID [HEX_FILE]]\n"))
54 (define (parse-arg1 args)
57 (let ((arg (car args)))
58 (if (exact-int? (string->number arg))
59 (parse-arg2 default-base args)
60 (parse-arg2 arg (cdr args))))))
62 (define (parse-arg2 base args)
65 (let ((arg (string->number (car args))))
66 (if (and (exact-int? arg)
69 (parse-arg3 base arg (cdr args))
72 (define (parse-arg3 base id args)
74 (parse-arg4 base id #f)
75 (let ((arg (car args)))
76 (if (null? (cdr args))
77 (parse-arg4 base id arg)
80 (define (parse-arg4 base id filename)
82 (start-client base id filename program-start-addr)
87 (define (exact-int? x)
88 (and (integer? x) (exact? x)))
92 (let ((connection-queue
94 (list port-number: port-number
96 eol-encoding: 'cr-lf))))
98 (serve (read connection-queue))
101 (define (start-client base id filename start-addr)
102 (set! program-start-addr start-addr)
104 (if (string=? base ".")
105 (receive (client server) (open-string-pipe)
110 (list server-address: base
111 port-number: port-number
112 eol-encoding: 'cr-lf)))))
114 (with-exception-catcher
118 (host-info-name (host-info "")))))
120 (let ((ack (read connection)))
121 (if (equal? ack '(ok))
125 (set! program-filename filename)
126 (start-client-upload connection id)))
127 (start-client-console connection id))
129 (list "Another client is already connected to robot " id "\n"))))
130 (close-port connection)))
132 (define (start-client-upload connection id)
133 (let ((mem (read-hex-file program-filename)))
135 (upload connection id mem program-start-addr))))
137 (define (start-client-console connection id)
139 (define (restart-robot)
140 (restart connection id)
141 (start-client-console connection id))
143 (define (upload-again)
144 (start-client-upload connection id)
145 (start-client-console connection id))
149 (start-client-console connection id))
152 (list "###\n### Console:\n"))
153 (let ((input (repl-input-port)))
155 (tty-mode-set! input #t #t #t #f 0)))
156 (let ((input (repl-input-port))
160 (let loop1 ((state 0))
161 (input-port-timeout-set! connection 0.01)
162 (let ((x (read connection)))
163 (if (not (eof-object? x))
164 (cond ((or (eq? x 'err)
166 (set! can-send-key #t))
170 (if (and (>= (u8vector-length x) 3)
171 (= (quotient (u8vector-ref x 0) nb-ids)
173 (let ((seq-num (u8vector-ref x 1)))
174 (if (not (= seq-num rx-seq-num))
176 (set! rx-seq-num seq-num)
178 (if (< i (u8vector-length x))
179 (let ((n (u8vector-ref x i)))
184 ((or (< n 32) (> n 126))
188 (write-char (integer->char n))))
189 (loop2 (+ i 1))))))))
190 (write (u8vector->list x))))))))
193 (input-port-timeout-set! input 0.01)
194 (let ((x (read-char input)))
198 (vector 'send-message
199 (+ id (* nb-ids MSG_TYPE_STDIO))
203 (set! can-send-key #f)
204 (set! tx-seq-num (modulo (+ tx-seq-num 1) 256))
208 (cond ((char=? x #\tab)
212 (cond ((char=? x #\u001b)
217 (cond ;((char=? x #\u001b))
225 (cond ((char=? x #\P) ; F1
236 (cond ((char=? x #\A)
249 ;------------------------------------------------------------------------------
251 (define (read-hex-file filename)
253 (define addr-width 32)
255 (define (syntax-error)
256 (error "Improper HEX file"))
259 (with-exception-catcher
263 (open-input-file filename)))))
265 (define mem (make-vector 16 #f))
267 (define (mem-store! a b)
270 (x (- addr-width 4)))
273 (let ((i (arithmetic-shift a (- x))))
274 (let ((v (vector-ref m i)))
276 (let ((v (make-vector 16 #f)))
279 (- a (arithmetic-shift i x))
284 (define (f m a n tail)
286 (define (g i a n tail)
288 (g (- i 1) (- a n) n (f (vector-ref m i) a n tail))
293 (cons (cons (- a 1) m) tail)
294 (g 15 a (quotient n 16) tail))
297 (f mem (expt 2 addr-width) (expt 2 addr-width) '()))
302 (define (read-hex-nibble)
303 (let ((c (read-char f)))
304 (cond ((and (char>=? c #\0) (char<=? c #\9))
305 (- (char->integer c) (char->integer #\0)))
306 ((and (char>=? c #\A) (char<=? c #\F))
307 (+ 10 (- (char->integer c) (char->integer #\A))))
308 ((and (char>=? c #\a) (char<=? c #\f))
309 (+ 10 (- (char->integer c) (char->integer #\a))))
313 (define (read-hex-byte)
314 (let* ((a (read-hex-nibble))
315 (b (read-hex-nibble)))
321 (let ((c (read-char f)))
322 (cond ((not (char? c)))
323 ((or (char=? c #\linefeed)
326 ((not (char=? c #\:))
329 (let* ((len (read-hex-byte))
332 (type (read-hex-byte)))
333 (let* ((adr (+ a2 (* 256 a1)))
334 (sum (+ len a1 a2 type)))
338 (let ((a (+ adr (* hi16 65536)))
341 (set! adr (modulo (+ adr 1) 65536))
350 (let* ((a1 (read-hex-byte))
351 (a2 (read-hex-byte)))
352 (set! sum (+ sum a1 a2))
353 (set! hi16 (+ a2 (* 256 a1)))))
356 (let ((check (read-hex-byte)))
357 (if (not (= (modulo (- sum) 256) check))
359 (let ((c (read-char f)))
360 (if (or (not (or (char=? c #\linefeed)
361 (char=? c #\return)))
370 (list "\n### The file " filename " does not exist\n"))
373 (define (upload connection id mem start-addr)
375 (define max-programmable-address 65535)
377 (define bp 8) ; program block size
378 (define be 64) ; erase block size
380 (if (start-programming connection id)
382 (let loop1 ((last-erased-be -1)
387 (a-bp (quotient a bp))
388 (a-be (quotient a be))
389 (bp-bytes (make-u8vector bp 255)))
390 (if (<= a max-programmable-address)
391 (if (or (= a-be last-erased-be)
392 (let ((a (* a-be be)))
394 (erase-block connection id a))))
396 (u8vector-set! bp-bytes (modulo a bp) (cdr x))
397 (let loop2 ((lst2 (cdr lst)))
398 (if (and (pair? lst2)
399 (let ((a (car (car lst2))))
400 (and (<= a max-programmable-address)
401 (= (quotient a bp) a-bp))))
403 (u8vector-set! bp-bytes
404 (modulo (car (car lst2)) bp)
407 (if (let ((a (* a-bp bp)))
409 (program-block connection id a bp-bytes)))
412 (reboot connection id)))
413 (reboot connection id))))))
415 (define (request cmd connection)
420 (let ((x (request-once cmd connection)))
429 (display " ERROR!\n")
432 (define (request-once cmd connection)
433 (send cmd connection)
435 (let ((x (read connection)))
436 (cond ((or (eq? x 'err)
442 (define (request-version connection id)
444 (define (version-msg? version)
445 (and (u8vector? version)
446 (= (u8vector-length version) 5)
447 (= (u8vector-ref version 1)
448 (quotient version-addr 256))
449 (= (u8vector-ref version 2)
450 (modulo version-addr 256))))
453 (input-port-timeout-set! connection #f)
456 (request-once (vector 'set-program-mode id) connection)
458 (vector 'send-message
459 (+ id (* nb-ids MSG_TYPE_PROGRAM))
460 (u8vector (quotient version-addr 256)
461 (modulo version-addr 256)
464 (input-port-timeout-set! connection 1)
467 (let ((x (read connection)))
468 (cond ((eof-object? x)
482 (loop ack version)))))))
484 (define (send obj port)
489 (define (start-programming connection id)
491 (list "\n### Programming robot " id " with " program-filename))
492 (enter-program-mode connection id))
494 (define (stop connection id)
496 (list "\n### Stopping robot " id))
497 (enter-program-mode connection id))
499 (define (restart connection id)
501 (list "###\n### Connecting to robot " id))
502 (enter-program-mode connection id)
503 (reboot connection id))
505 (define (enter-program-mode connection id)
510 (let ((version (request-version connection id)))
512 (let ((version-major (u8vector-ref version 3))
513 (version-minor (u8vector-ref version 4)))
514 (if (and (= version-major 1)
520 (display " INCOMPATIBLE FIRMWARE!\n")
524 (display " THE ROBOT IS NOT RESPONDING!\n")
527 (define (erase-block connection id addr)
528 ; (set! addr (+ addr #x2000))
530 (list "###\n### Erasing block 0x"
531 (number->string addr 16)))
533 (vector 'send-message
534 (+ id (* nb-ids MSG_TYPE_PROGRAM))
535 (u8vector (quotient addr 256)
539 (define (program-block connection id addr bytes)
540 ; (set! addr (+ addr #x2000))
542 (list "### Programming block 0x"
543 (number->string addr 16)))
545 (vector 'send-message
546 (+ id (* nb-ids MSG_TYPE_PROGRAM))
548 (u8vector (quotient addr 256)
553 (define (reboot connection id)
555 (list "###\n### Restarting robot"))
557 (vector 'send-message
558 (+ id (* nb-ids MSG_TYPE_PROGRAM))
562 ;------------------------------------------------------------------------------
569 (define multiplexer #f)
574 (set! mutex (make-mutex))
575 (set! clients (make-vector nb-ids #f))
576 (set! multiplexer (open-vector))
580 (list path: serial-port-name
581 eol-encoding: 'cr-lf)))
583 (tty-mode-set! rs232 #f #f #t #t 38400))
592 (input-port-timeout-set! multiplexer 0.01)
594 (let ((x (read multiplexer)))
596 (let* ((id (vector-ref x 0))
597 (cmd (vector-ref x 1))
598 (cmd-type (vector-ref cmd 0)))
599 (cond ((eq? cmd-type 'send-message)
600 (let ((dest (vector-ref cmd 1))
601 (bytes (vector-ref cmd 2)))
602 (if (send-message dest bytes)
603 (let ((s (wait-until-end-of-tx)))
607 ((not (= (bitwise-and s NOERR_MASK) 0))
608 (ir-tx-event-noerr-ack)
609 (send-to-id 'noerr id)
611 ((not (= (bitwise-and s ERR_MASK) 0))
612 (ir-tx-event-err-ack)
621 ((eq? cmd-type 'set-program-mode)
622 (let ((dest (vector-ref cmd 1)))
623 (if (set-program-mode dest)
624 (let ((s (wait-until-end-of-tx)))
628 ((not (= (bitwise-and s NOERR_MASK) 0))
629 (ir-tx-event-noerr-ack)
630 (send-to-id 'noerr id)
632 ((not (= (bitwise-and s ERR_MASK) 0))
633 (ir-tx-event-err-ack)
634 (send-to-id 'noerr id)
645 (poll-status-handling-rx)
648 (define (set-program-mode dest)
649 (let ((s (prepare-to-tx)))
651 (let ((b (+ dest (* nb-ids MSG_TYPE_SET_PROG_MODE))))
652 (ir-tx-special (- #xff b) b)))))
654 (define (send-message dest bytes)
655 (let ((s (prepare-to-tx)))
659 (list "sending to " (modulo dest nb-ids) ": "))
660 (write (u8vector->list bytes))
662 (ir-tx dest bytes)))))
664 (define (prepare-to-tx)
666 (let ((s (wait-until-end-of-tx)))
669 ((not (= (bitwise-and s NOERR_MASK) 0))
670 (ir-tx-event-noerr-ack)
672 ((not (= (bitwise-and s ERR_MASK) 0))
673 (ir-tx-event-err-ack)
678 (define (wait-until-end-of-tx)
680 (let ((s (poll-status-handling-rx)))
683 ((not (= (bitwise-and s TX_MASK) 0))
688 (define (poll-status-handling-rx)
690 (let ((s (poll-status)))
693 ((not (= (bitwise-and s RX_MASK) 0))
699 (define (handle-rx-message)
702 (let ((id (modulo (u8vector-ref msg 0) nb-ids)))
704 (list " received from " id ": "))
705 (write (u8vector->list msg))
707 (send-to-id msg id)))))
709 (define (send-to-id msg id)
711 (let ((client (vector-ref clients id)))
713 (with-exception-catcher
715 (vector-set! clients id #f))
717 (send msg client)))))
718 (mutex-unlock! mutex))
720 (define (ir-tx-event-noerr-ack) (send-command-no-cr "n" #t))
721 (define (ir-tx-event-err-ack) (send-command-no-cr "e" #t))
723 (define (send-command-no-cr cmd trace?)
724 (and (rs232-send-no-cr cmd trace?)
727 (define (send-command cmd trace?)
728 (and (rs232-send cmd trace?)
731 (define (check-ok trace?)
732 (let ((answer (rs232-read-line trace?)))
733 (and (string? answer)
734 (= (string-length answer) 1)
735 (char=? (string-ref answer 0) #\!))))
737 (define (byte->string n)
738 (define (hex n) (string-ref "0123456789ABCDEF" (modulo n 16)))
739 (string (hex (quotient n 16))
742 (define (ir-tx-special byte1 byte2)
747 (list byte1 byte2)))))
748 (send-command-no-cr cmd #t)))
750 (define (ir-tx dest bytes)
755 (cons dest (u8vector->list bytes))))))
756 (send-command cmd #t)))
758 (define (poll-status)
759 (and (rs232-send-no-cr "p" #t)
760 (let ((answer (rs232-read-line #t)))
761 (and (string? answer)
762 (= (string-length answer) 3)
763 (char=? (string-ref answer 0) #\=)
764 (string->number (substring answer 1 3) 16)))))
767 (and (rs232-send-no-cr "r" #t)
768 (let ((answer (rs232-read-line #t)))
769 (and (string? answer)
770 (>= (string-length answer) 3)
771 (odd? (string-length answer))
772 (char=? (string-ref answer 0) #\=)
773 (let ((n (quotient (string-length answer) 2)))
774 (let ((v (make-u8vector n 0)))
775 (let loop ((i (- n 1)))
777 (let* ((j (+ (* i 2) 1))
779 (substring answer j (+ j 2))
783 (u8vector-set! v i x)
787 (define MSG_TYPE_ACK 0)
788 (define MSG_TYPE_SET_PROG_MODE 1)
789 (define MSG_TYPE_NORMAL 0)
790 (define MSG_TYPE_PROGRAM 1)
791 (define MSG_TYPE_STDIO 7)
793 (define NOERR_MASK 1)
796 (define CLOCK_MASK 8)
799 (define (rs232-flush-input)
800 (input-port-timeout-set! rs232 0)
801 (read-line rs232 #f))
803 (define no-response-count 0)
805 (define (rs232-read-line trace?)
806 (input-port-timeout-set! rs232 0.5)
807 (let ((x (read-line rs232)))
809 (if (and debug? trace?)
810 (pp (list '(rs232-read-line) '-> x)))
813 (set! no-response-count (+ no-response-count 1))
814 (if (> no-response-count 100)
816 (pp 'base-station-not-responding)
817 (set! no-response-count 50))))
819 (if (and debug? trace?)
824 (if (>= no-response-count 50)
825 (pp 'base-station-now-ok))
826 (set! no-response-count 0)))
829 (define (rs232-send-no-check str trace?)
831 (if (and debug? trace?)
832 (pp (list 'rs232-send-no-check str)))
836 (if (and debug? trace?)
842 (define (rs232-send-no-cr-no-check str trace?)
844 (if (and debug? trace?)
845 (pp (list 'rs232-send-no-cr-no-check str)))
848 (if (and debug? trace?)
854 (define (rs232-send str trace?)
855 (rs232-send-no-check str trace?)
856 (let ((echo (rs232-read-line #f)))
857 (if (and debug? trace? (string? echo))
863 (string=? echo str))))
865 (define (rs232-send-no-cr str trace?)
866 (rs232-send-no-cr-no-check str trace?)
867 (let ((echo (rs232-read-line trace?)))
869 (string=? echo str))))
871 (define (serve connection)
875 (let ((id-and-hostname (read connection)))
876 (if (and (pair? id-and-hostname)
877 (pair? (cdr id-and-hostname))
878 (null? (cddr id-and-hostname))
879 (exact-int? (car id-and-hostname))
880 (>= (car id-and-hostname) 0)
881 (< (car id-and-hostname) nb-ids))
882 (let ((id (car id-and-hostname))
883 (hostname (cadr id-and-hostname)))
885 (let ((client (vector-ref clients id)))
888 (mutex-unlock! mutex)
890 (list "============================================= connection to robot " id " from " hostname " **REFUSED**\n"))
894 (list "============================================= connection to robot " id " from " hostname " **REFUSED**\n")
896 (force-output log-file)))
897 (close-port connection))
899 (vector-set! clients id connection)
900 (mutex-unlock! mutex)
902 (list "============================================= connection to robot " id " from " hostname "\n"))
906 (list "============================================= connection to robot " id " from " hostname "\n")
908 (force-output log-file)))
909 (send '(ok) connection)
910 (process-client-commands connection id)
912 (vector-set! clients id #f)
913 (mutex-unlock! mutex)
914 (close-port connection)))))))))))
916 (define (process-client-commands connection id)
917 (with-exception-catcher
922 (let ((cmd (read connection)))
925 (send (vector id cmd) multiplexer)
928 ;------------------------------------------------------------------------------