#lang racket (require racket/tcp) (require json) (define serial-port-device "/dev/ttyUSB0") (define port 4444) (define hostname "127.0.0.1") ; Send a json-expr on the output, prepending it with 4 bytes that contain the JSON length (define (send-json json-expr-to-send output) (write-bytes (integer->integer-bytes (bytes-length (jsexpr->bytes json-expr-to-send)) 4 #f) output) (write-json json-expr-to-send output)) ; 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 (define (send-pizzas-list output) (send-json (get-all-pizzas) 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 price) (let ((str "")) (for-each (lambda (pizza-hashmap) (set! str (string-append str (format "~a : ~s\n" (hash-ref pizza-hashmap 'quantity) (hash-ref pizza-hashmap 'name))))) pizzas-list) (set! str (string-append str (format "Total price: ~a\n" price))) str)) ; Get the price of each type of pizza and calculate the total price (define (get-total-price pizzas-list) (let ((price 0) (all-pizzas-list (hash-ref (get-all-pizzas) 'pizzas))) (for-each (lambda (pizza-hashmap) (set! price (+ price (get-price-from-pizza pizza-hashmap all-pizzas-list)))) pizzas-list) price)) ; Create a format string with the received pizza list, calculate the ; price of the whole order, print them and send it back to the client (define (print-pizzas-and-send-price pizzas-list output) (let* ((total-price (get-total-price pizzas-list))) (printer-print (format-pizza pizzas-list total-price)) (send-json (hash 'total_price total-price) output))) ; 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") (print-pizzas-and-send-price (hash-ref parsed-json 'pizzas) output)) (("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)