Монитор заказов вместо кухонного термопринтера

Монитор заказов

Небольшой квест о замене кухонного принтера заказов в ресторане на табло заказов 24″ монитор с raspberryPi за вечер. Это актуально практически для любой системы erp (все современные 1С системы в торговом оборудовании поддерживают чековые принтеры, аналогично и с другими системами).

Ремарка

В ресторанах и кафе для печати заказов на кухне чаще всего используют принтеры заказов (принтеры «марок»). Это небольшие термопринтеры (родственники контрольно-кассовых машин), но без фискальных накопителей, и кнопка у них чаще всего одна — промотка ленты. Раньше термопринтеры были преимущественно связаны с системами типа FrontOffice по COM порту, но около 10 лет назад ситуация изменилась, в принтерах появилась поддержка Ethernet.

Опыт

Принтеры, которые встречались в работе производителей Штрих-М, Posiflex, Sam4s, однотипны, используют для печати протокол RAW (Протокол односторонний). У них есть небольшие веб-серверы с настройками скорости печати, указания порта, кодировки, дополнительные функциональные возможности и настройки сети. Некоторые модели имеют возможность подключения сканера штрихкодов для уведомлений о готовности блюд(пересылают штрихкод в сеть). Стоимость на текущий день для бюджетных моделей начинается от 10 т.р. и может доходить до 30 т.р на Epson. Срок жизни при интенсивной эксплуатации от пары лет. Основные причины выхода из строя — поломка отрезчика бумаги, жир (покрывает принтер снаружи и частично механизмы внутри), отказ термоголовки, высыхание пластмассы роликов и шестеренок, залитие принтеров жидкостями. Ремонт и замена элементов составляет от 50% стоимости принтера, плюс, конечно же, расходный материал — термобумага.

Задача

Итак, по согласованию с кухней и администрацией взамен очередного вышедшего из строя термопринтера был смонтирован монитор с raspberry pi 3 B c sd-картой на 2 Гб.
Основная задача не вносить изменений в FrontOffice систему, и для ПО не отличаться от принтера чеков/заказов.

ПО официантов FrontOffice Штрих-М, в качестве принтера заказов указан Штрих-600. Ранее, когда менялись российские принтеры на корейские, выяснилось, что кодовая страница, в которой передаются пакеты, — это Windows-1251 порт 9100.

Выбор и настройка ОС

В качестве мини ПК будет Raspberry Pi 3 Model B, развернем а нем легковесную систему Raspbian Stretch Lite.

Проведем небольшой тюнинг: доставим в систему менеджер окон openbox, менеджер входа в систему LightDM, настроим автологин, скроем лог загрузки.

Немного анализа

Далее построим простенький сокет-сервер, чтобы узнать, как информация кодируется в пакете, и что там вообще отправляется на термопринтер.

#!/usr/bin/env python # -*- coding: utf-8 -*-  import socket  sock = socket.socket() sock.bind(('', 9100)) sock.listen(1)   while True:     conn, addr = sock.accept()     data = conn.recv(16384)     print(data)     # print(((data.rsplit('*',1)[1]).rsplit('- -',9)[0]).decode('cp1251').encode('utf8'))     # clear_data = ((data.rsplit('*',1)[1]).rsplit('- -',9)[0]).decode('cp1251').encode('utf8')     conn.close() 

ПО FrontOffice отправляет данные одним пакетом в котором летит пачка спец. символов перед основной частью и после неё. Справочная информация о шрифтах и их размере кодирована символами, которых нет в кодировке utf8. После каждой строки указан перенос /r/n. Можно было написать функцию, фильтрующую спец.символы, но у нас один вечер, а в «марке» очень удачно отделено начало строкой звездочек, конец строкой символов минус. Добавим костыль, отбросим спец символы в начале и конце, декодируем в utf8. В окне консоли получим чек, как он есть при печати на «марке» из принтера.

Архитектура будущего приложения

Прикинем немного архитектуру приложения.

  1. Сокет-сервер, постоянно ожидающий прием.
  2. Веб-сервер.
  3. Приложение просмотра — браузер с fullscreen.
  4. Система обмена сообщениями между сокет-сервером и веб-сервером.

Продакшн

Первый и четвертый пункт решим, дополнив выше написанный сокет-сервер — redis — хранилищем ключ-значение, с прицелом на будущую доработку( каналы — подписки), попутно снизим износ sd-карты. И добавим сигнал — уведомление о приходе нового заказа, воспроизводить будем через hdmi на колонках монитора. Вывод звука активируем через raspi-config.

 #!/usr/bin/env python # -*- coding: utf-8 -*-  import socket import redis import pygame  sock = socket.socket() sock.bind(('', 9100)) sock.listen(1) pygame.mixer.pre_init(frequency=44100, size=-16, channels=2, buffer=4096) pygame.mixer.init(44100, -16, 2, 4096) sound = pygame.mixer.Sound("icq.wav") #print(sound.get_num_channels())  r = redis.StrictRedis(host='localhost', port=6379, db=0) n=0  while True:     conn, addr = sock.accept()     data = conn.recv(16384)     print(((data.rsplit('*',1)[1]).rsplit('- -',9)[0]).decode('cp1251').encode('utf8'))     sound.play()     clear_data = ((data.rsplit('*',1)[1]).rsplit('- -',9)[0]).decode('cp1251').encode('utf8')     r.set('data'+str(n), clear_data)     n=n+1     conn.close() 

По второму пункту накидаем веб-сервер на flask с автообновление каждые 15 секунд (пока это самый простой вариант), в таск-лист пометим socketio и очередь возможно celery или на redis. Переберем все доступные пары ключ — значение и отобразим на страничке. По клику на «марке» удалим из redis и с рабочего стола соответственно.

 # -*- coding: utf-8 -*- from flask import Flask, render_template, redirect import os import redis  r = redis.StrictRedis(host='localhost', port=6379, db=0)  app = Flask(__name__)   def kernel_ver():     try:         f = open(os.path.dirname(os.path.abspath(__file__)) + '/release.txt')         lines = f.readlines()         f.close()         return lines[0]     except IOError as e:         return "--"   @app.route('/') def index():     d = {}     for item in r.keys():         d[item] = (r.get(item)).decode('utf8')     return render_template("index.html", release=kernel_ver(), di = d)  @app.route('/del/<key>') def delstamp(key):     r.delete(key)     return redirect("http://192.168.1.80:5000/", code=302)   if __name__ == "__main__":     app.run(host='0.0.0.0') 

Добавим jinja шаблон

<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta http-equiv="refresh" content="30"/> <title> Монитор заказов</title> <link  href="http://fonts.googleapis.com/css?family=Reenie+Beanie:regular" rel="stylesheet" type="text/css"> </head> <body>   <ul>     {% for key in di %}     <li>         <a href="/del/{{key}}">         <!-- h2>Title #1</h2 -->         {% for item in di[key].splitlines() %}         <p>{{ item }}</p>         {% endfor %}       </a>     </li>     {% endfor %}   </ul> </body> </html> 

Остался пункт 3, сделаем самый минимальный браузер без кнопок из 13 строк.

 import sys from PySide import QtCore, QtGui, QtWebKit  class MainWindow(QtGui.QMainWindow):     def __init__(self):         super(MainWindow, self).__init__()         self.showFullScreen()         self.web = QtWebKit.QWebView(self)         self.web.load(QtCore.QUrl('http://127.0.0.1:5000'))         self.setCentralWidget(self.web)   app = QtGui.QApplication(sys.argv) main_window = MainWindow() main_window.show() sys.exit(app.exec_()) 

Далее необходимо создать сервисы для запуска всех выше написанных скриптов.
Или по-быстрому их прописать в autostart файл openbox.

Результат

Кухонный монитор заказов
FavoriteLoadingДобавить в избранное
Posted in Без рубрики

Добавить комментарий