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

Небольшой квест о замене кухонного принтера заказов в ресторане на табло заказов 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.

Результат
Кухонный монитор заказов

Комментарии (38)


  1. jonic
    03.08.2018 17:07
    +1

    А почему «Елен\nа Геннадиевна»? В чем прикол повару смотреть на стикер, почему нельзя было согратить шапку до:
    12:30:05 №123
    Ресторан №03
    Н. Елена Геннадиевна
    ==============
    5 х Название блюда


    1. DarkDemon Автор
      03.08.2018 17:14

      Сложно сказать есть два варианта. Первый это длина строки 24 символа и по мнению 1с что то не помещается и она так подробила. Можно указать любую длину, но что бы листочки помещались на экране была выбрана 24 символа в 1с. Второй вариант по какой то причине 1с поставила там перенос. Дальше я вообще думаю парсить и переформатировать весь чек на этой стороне.


      1. jonic
        03.08.2018 17:51

        парсинг тут точно не помешает, опрятность прежде всего :) с этим же работают люди, которые может и не подозревают что так можно сделать


    1. DarkDemon Автор
      03.08.2018 17:22
      +1

      Ну и в тексте я уточнил, что я не вносил никаких изменений в FrontOffice который этот чек формирует.


  1. REPISOT
    03.08.2018 17:22
    +3

    Основная задача не вносить изменений в FrontOffice систему, и для ПО не отличаться от принтера чеков/заказов.
    Задача, все же — показывать заказы.
    А то, что вы обозвали основной задачей — всего лишь дополнительное требование.


  1. ILya63
    03.08.2018 17:24

    Работникам кухни, если это не кухня Макдональдса или подобная, проще всё-таки работать с принтером. Кухней бывает несколько в одном ресторане (горячая, холодная, кондитерская) и система настраивается таким образом, что при неудачной печати на одном принтере, чек будет отправлен на резервный принтер, то есть на другую кухню и работник этой кухни передаст чек куда надо.

    Принтеры Epson стоят возможно дороже аналогов, но они работают по 25 лет при очень редком обслуживании.


    1. DarkDemon Автор
      03.08.2018 17:31
      +1

      Принтеры Epson работает хорошо. Стоят космически. Основные поломки вызывает конечно персонал при печати клин ножа, который затем надо крутить через винт отверткой. Персонал обычно просто пытается открыть его в результате залом ножа — ремонт не дешевый. У принтеров есть свои преимущества и недостатки. В данном случае кухня сама так попросила и пока довольна. Ну и я не первый обратите внимание на r-keeper, iiko и т.д. У них так же продаются аналогичные мониторы с ПО под Windows.


      1. ILya63
        03.08.2018 17:49

        Если реализовать возможность звукового оповещения о новом заказе кухня будет довольна еще больше.
        Обычно чеки из принтера подкладывают под блюда или прикалывают рядом (над блюдом) на столе выдачи, что бы официант мог определить блюдо из конкретного заказа. Как это реализовано в случае с монитором и отсутствия чеков?


        1. DarkDemon Автор
          03.08.2018 17:56
          +1

          И добавим сигнал — уведомление о приходе нового заказа, воспроизводить будем через hdmi на колонках монитора. Вывод звука активируем через raspi-config.

          Кухня не прикладывает чеки, официанты и так хорошо знают что и кому. Это проблема памяти официантов.


          1. lifestar
            04.08.2018 10:41

            Ситуация. Два стола заказали одно и тоже блюдо с разницей по времени 10 минут, сначала первый потом второй. У столов разные официанты.
            Официант второго стола, не имея инфы кому блюдо, запросто может унести «блюдо для первого стола» на второй. В результате гости с первого стола будут ждать свой заказ дольше обычного.
            Вот для чего в основном прикладывают чеки (особенно актуально в прайм-тайм)


            1. DarkDemon Автор
              04.08.2018 10:55

              Для этого существует обратная связь от кухни к официантам о готовности блюд.


            1. DarkDemon Автор
              04.08.2018 10:59

              С чеками тоже не все так просто, если ресторан большой и на нем например 5 кухонь. Если их положить на поднос рядом с блюдом они улетают с него еще на этапе погрузки в лифт. Подкладывать под блюдо можно, но если чек маленький его не видно. Да и официанту к разным выдачам лишние круги наматывать, причем в цикле.


          1. chapter_one
            04.08.2018 23:40

            Это проблема официантов или в очень мелких заведениях, или в местах, где руководству плевать на эффективность. Во всех других местах и кухня и бар в обязательном порядке на выдаче маркируют заказы тем или иным способом. И бумажный «чек» — до сих пор один из самых простых, дешевых и удобных способов.


        1. ILya63
          03.08.2018 17:56

          Ой не заметил, что оповещение есть.


  1. ProstoTyoma
    03.08.2018 20:01
    +3

    А не лучше ли было сделать без дизайнерских изысков, максимально контрастно и информативно, примерно как в бургер кинге, например. Или это и есть то самое «и для ПО не отличаться от принтера чеков/заказов»? image


    1. DarkDemon Автор
      03.08.2018 20:43

      Я смотрел дизайны, мне показалось что подобного типа дизайны слишком контрастные. Вы бы смогли смотреть на яркие DOS тона целый день. Думаю скорее так исторически сложилось. Возможно эти системы и писались под DOS.


      1. daggert
        03.08.2018 23:08
        -1

        А на них не надо смотреть целый день. На них надо глянуть мимолетно и точно понять что требуется приготовить.


  1. DarkDemon Автор
    04.08.2018 00:13
    +2

    Так выглядит чек с принтера
    image


    1. jonic
      04.08.2018 11:48
      +1

      Но везде текст ровный и крупный, про это и говорил ProstoTyoma


  1. dmitryrf
    04.08.2018 12:39

    Поясните пожалуйста, откуда берется фон и повернутые под углом листочки? В шаблоне ничего такого не увидел.
    И еще:

    веб-сервер на flask с автообновление каждые 15

    Но сервер же ничего не обновляет, это клиент ходит к нему каждые 30 секунд?
    meta http-equiv=«refresh» content=«30»


    1. DarkDemon Автор
      04.08.2018 12:47

      Да все верно, в коде 30 сек, сейчас сократил до 15 сек.css файл не подкрепил со стилями.


      1. dmitryrf
        04.08.2018 12:48

        Теперь всё ясно, спасибо!


  1. Vlad5
    04.08.2018 13:04

    Перечитал 2 раза, но так и не понял при чём тут ERP?

    Это актуально практически для любой системы erp (все современные 1С системы в торговом оборудовании поддерживают чековые принтеры, аналогично и с другими системами).
    Есть полно ERP систем, которые как-бы и не поддерживают торговое оборудование, не их это задача, или поддерживают через костыли.


    1. DarkDemon Автор
      04.08.2018 13:28

      Возможно и есть изолированные экосистемы erp, но навскидку таких не помню. Приведите пример.


      1. Vlad5
        04.08.2018 14:20

        Изолированные от чего? По вашей логике получается — давайте натянем erp-систему на принтер, а потом откажемся от принтера и заменим его на моник. Если по функционалу требуется печать на принтерах. будет дано ТЗ на разработку функционала и всё будет печататься, выводиться на нужные девайсы в нужном виде.


        1. DarkDemon Автор
          04.08.2018 16:03

          Объясните это разработчикам проприетарных систем без возможности изменений последних. На сколько возрастет стоимость разработки? Вы говорите о корпорациях? С малым бизнесом все несколько иначе.


          1. DarkDemon Автор
            04.08.2018 16:09

            Я не вижу надобности сгородить еще один формат обмена. Данных выдаваемых софтом вполне достаточно. Выше я уже сказал что позже я приведу стикеры к удобочитаемому виду.


          1. Vlad5
            04.08.2018 18:02

            Да, именно про проприетарные системы речь и идёт. Ведь именно из них на 99% состоит весь объём внедрений erp-систем. И творение от 1С более чем относится к проприетарному софту, учитывая её политику продаж, лицензирования и отслеживания «легальности» их программ.
            Что касается внедрения систем такого класса на малые предприятия, у меня возникает вопрос — что это за предприятия такие «малые»? Допускаю конечно такую модель, что некая компания на аутсорсе ведёт дела крупной компании в erp-системе (может предоставлять штат операторов, администраторов, программистов), но сама-то она пользуется простенькой системой для ведения своих дел.


  1. vvzvlad
    04.08.2018 23:53

    Интересно посмотреть на мнение тех, кто с этой системой теперь работает. Сдается мне, листочки удобнее.


    1. DarkDemon Автор
      05.08.2018 00:32

      Работают уже пару недель в двух независимых организациях. Отзывы положительные. Просят расширить функционал.


  1. smilyfox
    05.08.2018 01:10

    Как работник взаимодействует с монитором заказов? Кликает мышкой? Неудобно наверное?


    1. DarkDemon Автор
      05.08.2018 21:10

      Пока мышью. Есть специализированные 12 клавишные контроллеры. Есть задумка с энкодером и парой кнопок.


      1. smilyfox
        05.08.2018 22:36

        Можно поставить цифровую клавиатуру как на кассе, у нее и цена символическая — около 400 рублей. Энкодеры, как дешевая альтернатива тачскрину, неплохо себя показали в киосках. Но, вам же понадобится что-то влаго-жирозащищенное?


        1. DarkDemon Автор
          05.08.2018 22:52

          Да конечно влага-жирозащищенное. Энкодер + эмулятор нескольких клавиш на arduino + корпус влагозащищенный будет аналогично по стоимости.


  1. batja84
    05.08.2018 01:30

    Что делать, если 3 одинаковых блюда идут на три разных стола? Выносит блюда не тот официант, которых их пробивал. Если есть принтер, то чек лежит рядом с тарелкой и понятно на какой стол нести. А так что делать, экран рядом с тарелкой класть? Тарелок рядом может и пять и восемь стоять. Никакого места не напасёшься. Новенькие работники могут не знать названий всех блюд, но если рядом лежит чек он в состоянии прочитать что это. Как этот вопрос решит экран?


    1. DarkDemon Автор
      05.08.2018 21:13

      Выше уже обсуждалось. В нормальных заведениях официанта не допустят до работы пока он не будет знать ассортимент блюд.


  1. AlexPublic
    05.08.2018 17:12
    +1

    Дааа, классический пример. Похоже что тут веб-разработчик решил решить простенькую десктопную задачку, но вместо того чтобы изучить существующий для этого инструментарий, он притащил на десктоп весь свой серверный стек. По нормальному тут очевидно не нужен ни redis, ни браузер, ни внутренний веб-сервер. Изначальное приложение автора (для прослушивания сокета) элементарно расширяется до требуемой ему функциональности буквально за десяток строчек кода (если использовать какую-то из приличных десктопных GUI-библиотек). Т.е. по факту имеем дикий оверинжениринг и трату ресурсов железа впустую. Однако для данного проекта это всё видимо не принципиально, так что можно наверное и его назвать успешным…


    1. DarkDemon Автор
      05.08.2018 21:24

      Приведите пример 10 строчек кросплатформенного приложения. Не забываем про гибкость дизайна и гибкость его изменения. Это все лишь микросервисная архитектура. Фактически это аналог разработки на Electron, только на python. Redis позволяет иным приложениям получать доступ к тем же данным ( парралельный доступ), я упоминул в статье подписки.