Scontrini-Daemon/scontrini.rkt

95 lines
3.7 KiB
Racket
Raw Permalink Normal View History

2023-10-25 17:45:57 +02:00
#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")))
2023-10-25 19:54:45 +02:00
(read-json file)))
; Send the list of all pizzas as a JSON
(define (send-pizzas-list output)
(send-json (get-all-pizzas) output))
2023-10-25 17:45:57 +02:00
; 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)
2023-10-28 00:41:27 +02:00
(all-pizzas-list (hash-ref (get-all-pizzas) 'pizzas)))
2023-10-25 17:45:57 +02:00
(for-each (lambda (pizza-hashmap)
(set! price (+ price (get-price-from-pizza pizza-hashmap all-pizzas-list))))
2023-10-25 17:45:57 +02:00
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)))
2023-10-25 17:45:57 +02:00
; Parse the commands given in the JSON
2023-10-25 19:54:45 +02:00
(define (parse-json message output)
2023-10-25 17:45:57 +02:00
(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")))))
2023-10-25 17:45:57 +02:00
; 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
2023-10-25 19:54:45 +02:00
(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)))
2023-10-25 17:45:57 +02:00
; 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)))
2023-10-25 19:54:45 +02:00
(thread (lambda () (execute-commands input output)))
2023-10-25 17:45:57 +02:00
(infinite-loop)))))
(wait-for-connection)