#lang racket (require racket/tcp) (require json) (define serial-port-device "/dev/ttyUSB0") (define port 4444) (define hostname "127.0.0.1") ; Return a list of all the pizzas from the local JSON file (define (get-all-pizzas) (let ((file (open-input-file "pizzas_list.json"))) (read-json file))) ; Send the list of all pizzas as a JSON, prepending it with 4 bytes that contain the list length (define (send-pizzas-list output) (let ((all-pizzas-list (get-all-pizzas))) (write-bytes (integer->integer-bytes (bytes-length (jsexpr->bytes all-pizzas-list)) 4 #f) output) (write-json all-pizzas-list output))) ; Print the to-print string on the printer ; TODO add synchronization to avoid race conditions (define (printer-print to-print) (let ((file (open-output-file serial-port-device #:exists 'append))) (display (string-replace to-print "\n" "\n\n") file) ; always add an extra newline because the printer is a little messed up (close-output-port file))) ; Return the price of the pizza * its quantity, searching in all-pizzas-list (define (get-price-from-pizza pizza all-pizzas-list) ; TODO rewrite in a more idiomatic way (let ((found-price 0)) (for-each (lambda (pizza-hashmap) (if (equal? (hash-ref pizza 'name) (hash-ref pizza-hashmap 'name)) (set! found-price (hash-ref pizza-hashmap 'price)) #f)) all-pizzas-list) (* found-price (hash-ref pizza 'quantity)))) ; Format the pizzas list coming from the client in a human-readable way (define (format-pizza pizzas-list) (let ((str "") (price 0) (all-pizzas-list (hash-ref (get-all-pizzas) 'pizzas))) (for-each (lambda (pizza-hashmap) (set! str (string-append str (format "~a : ~s\n" (hash-ref pizza-hashmap 'quantity) (hash-ref pizza-hashmap 'name)))) (set! price (+ price (get-price-from-pizza pizza-hashmap all-pizzas-list)))) pizzas-list) (set! str (string-append str (format "Total price ~a" price))) str)) ; Parse the commands given in the JSON (define (parse-json message output) (let ((parsed-json (with-input-from-string message (lambda () (read-json))))) ; parse the JSON (case (hash-ref parsed-json 'command) (("print") (printer-print (hash-ref parsed-json 'text))) (("pizza") (printer-print (format-pizza (hash-ref parsed-json 'pizzas)))) (("get-pizzas") (send-pizzas-list output)) (else (displayln "Unknown command"))))) ; Parse the JSON and execute the command specified in it ; ; Each message starts with the length of the JSON object, put into the first 4 bytes (define (execute-commands input output) ; read the first 4 bytes, little-endian, unsigned (let ((message-length (integer-bytes->integer (read-bytes 4 input) #f))) (parse-json (bytes->string/utf-8 (read-bytes message-length input)) output) (close-input-port input) (close-output-port output))) ; Wait on a port and spawn new threads on each connection (define (wait-for-connection) (let ((listener (tcp-listen port 4 #t hostname))) (let infinite-loop () (let-values (((input output) (tcp-accept listener))) (thread (lambda () (execute-commands input output))) (infinite-loop))))) (wait-for-connection)