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


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

Из полезной информации есть только название модели "AW-5890C".

Это усложнило весь процесс, но я решил пойти с малого. Первой задачей было синхронизироваться через браузер с USB девайсом, посредством USB API.

И тут меня ждал подвох...
Казалось бы, всем знакомый (тем кто работал с USB в браузере) сниппет кода, для открытия соединения с USB девайсом:

navigator.usb.requestDevice({filters: []})
	.then(usbDevice => {
    
    usbDevice.open()
    	.then(() => console.log('Success'))
    	.catch(() => console.error('Bye bye'));
    
  });

В первой строке мы запрашиваем все девайсы в браузере, что-бы пользователь мог выбрать девайс. (Свойство "filters" обязательное как аргумент, потому пустой массив)
После того, как пользователь выбрал, мы вызываем у девайса метод "open", что-бы открыть соединение с ним.

В результате:

Видим наше устройство, уже отлично, выбираем его и....

"Это победа!"

На поиск истины у меня ушло более чем несколько часов, и вот она:
Windows 10 - единоличная операционная система...

Оказывается, если вы подключили какое-либо устройство и система определила его периферический тип и установила драйвера, то она тут же добавляет его в свою особенную службу, тем самым, делая устройство "занятым".

Это говорит о том, что вы можете послать файл на распечатку в принтер, который определился системой, но вы не можете воспользоваться USB портом, т.к. он занят принтером, который определила система.

Об этом же и говорит ошибка на скриншоте - "Access denied", т.к. устройство занято другим приложением (правильнее сказать службой операционной системы)

Решение этой проблемы довольно топорное, существует данная тулза, которая позволяет пере прошить драйвер USB, что-бы никакие службы не воровали устройство в свои владения.

Принцип прост, скачиваем, запускаем, выбираем наш порт, к которому подключено устройство, нажимаем "Install Driver"

На этом проблема исправилась, доступ к устройству был получен.


Следующий шаг был понять, как напечатать на принтере хоть что-нибудь...

На страничках гугла, stack overflow и т.п. была найдена информация что данные принтеры работают на 8 битных словах и можно просто отправить буфер 8 битных слов на принтер.
Так и было сделано:

// Encode
let encoder = new TextEncoder();
let buffer = encoder.encode('\n\n\nHello world\n\n\n');

// Input endpoint
const endpoint = usbDevice.configuration.interfaces[0].alternate.endpoints[0].endpointNumber;

// Print
usbDevice.transferOut(endpoint, buffer)
.catch(error => { console.warn(error); })

Результат меня знатно заинтриговал тем, что он сработал!

Еще стоит пояснить за то, что я использовал "Input" канал для отправки сообщения в коде свыше: почти каждое USB устройство имеет по крайней мере 2 канала - Ввод и вывод.

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


Радости моей не было конца и края!
Почти...

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

После такого, я начал задумываться о поиске документации для no name принтера.

Забив его модель в гугл и сопоставив все факты, а именно, что я живу в Украине и этот принтер владельцы соседнего магазина продали мне за гроши (как вероятно его сами и купили), то поиск не заставил себя долго ждать и был найден данный производитель.

У него в наличии есть практически такой же принтер с отличием в названии в 1 букву, а у них на сайте, так же можно найти и PDF с описанием контроллера и команд, которые я мог бы использовать.

Так же, по мимо этого, я нашел и документацию от более известного производителя HP

Но как базу я стал использовать PDF отечественного производителя, т.к. она показалась мне более достоверной в моих условиях.

После быстрого прочтения обеих документаций было найдено несколько интересных особенностей данных принтеров:

  1. Они все работают на 8 битных словах

  2. Они стековые

  3. Если встречается слово "0x1b", то оно распознается как начало команды.

В итоге была найдена данная команда:

Однако у HP список включал в себя на 9 кодировок больше:

Судя по описанию команда выглядит как 3 последовательных слова, которые должны быть отправлены на девайс, а именно:

  • 0x1B, 0x74, charset - в HEX формате.

  • 27, 116, charset - в десятичном формате.

Осталось выбрать кодировку и в путь!
Точкой опоры естественно стали кодировки с первого скриншота, которые относятся к принтерам Украинского производства.
Рассматривались различные кодировки, желательно те, которые участвовали бы в обеих списках, что-бы при изменении принтера на другой, не пришлось переписывать либо же сильно дополнять существующий код.


В качестве нужной кодировки у многих программистов взгляд мог сразу упасть на WPC 1252.

Но не тут то было, чистый обман глаз и никакого мошенничества.

Все помнят Notepad++ и его фишку с кодировками, но та самая что с русскими буквами и многим интересна есть 1251, в тот час как 1252 не содержит ничего интересного, кроме символов "Торговая марка" и нескольких валют, в ней даже знака Евро нет...

По данной причине взгляд пал на "PC866", она есть как в первом PDF так ив PDF от HP, что уже внушает доверие, причем в документе от HP она идет с приписью "Russian"

Что-бы установить кодировку, просто стоит отправить команду на девайс перед отправкой текста, выглядит это так:

// Encode
let encoder = new TextEncoder();
let buffer = encoder.encode('\n\n\nHello world\n\n\n');

const endpoint = usbDevice.configuration.interfaces[0].alternate.endpoints[0].endpointNumber;

// Set 866
usbDevice.transferOut(endpoint, new Uint8Array([0x1b, 0x74, 17]))
.catch(error => { console.warn(error); })

// Print
usbDevice.transferOut(endpoint, data)
.catch(error => { console.warn(error); })

Стоит напомнить, что данные принтеры являются стековыми, что дает нам возможность записать предыдущий снипет кода в таком виде:

// Encode
let encoder = new TextEncoder();
let buffer = encoder.encode('\n\n\nHello world\n\n\n');

const endpoint = usbDevice.configuration.interfaces[0].alternate.endpoints[0].endpointNumber;

// Set 866
usbDevice.transferOut(endpoint, new Uint8Array([0x1b]))
.catch(error => { console.warn(error); })

usbDevice.transferOut(endpoint, new Uint8Array([0x74]))
.catch(error => { console.warn(error); })

usbDevice.transferOut(endpoint, new Uint8Array([17]))
.catch(error => { console.warn(error); })

// Print
usbDevice.transferOut(endpoint, data)
.catch(error => { console.warn(error); })

Данный факт является и проблемой в то же время, одна неверно описанная команда в вашем коде, ложит принтер.

Если вы ошиблись с кол-вом аргументов, то вы получите излишние аргументы на печать, а недостающие будут съедены из данных которые вы пошлете после.

Как результат кода приведенного выше, русских букв я так и не увидел...

Стоит напомнить, что данный принтер работает на 8 битных словах.

А буква "П" в фразе "Привет мир" имеет индекс 1055 в юникоде, что не совсем умещается в диапазон 0-255 так ведь?

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

И тут я потратил еще минут 10 на составление функции для перевода текста из JavaScript в 866 кодировку, я учел в ней только русские буквы, остальные символы в учет не брал:

function utf8_to_866 (aa) {
  let c = 0;
  let ab = new Uint8Array(aa.length);

  for (let i = 0; i < aa.length; i++) {
      c = aa.charCodeAt(i);
      if (c >= 1040 && c <= 1087) {
        ab[i] = c - 912;
      } else if (c >= 1088 && c <= 1105) {
        ab[i] = c - 864;
      } else {
        ab[i] = aa.charCodeAt(i);
      }
  }
  return ab;
}

И в боевом окружении выглядит это так:

let data = utf8_to_866('\n\nПривет Хабр!\n\n\n\n\n\n\n\n');

const endpoint = usbDevice.configuration.interfaces[0].alternate.endpoints[0].endpointNumber;

// Set 866
usbDevice.transferOut(endpoint, new Uint8Array([0x1b, 0x74, 17]))
.catch(error => { console.warn(error); })

// Print
usbDevice.transferOut(endpoint, data)
.catch(error => { console.warn(error); })

Как результат:


Всем спасибо за внимание!


Список использованной литературы и приложений:
http://h10032.www1.hp.com/ctg/Manual/c06481647.pdf
https://www.xprinter.com.ua/image/data/tovar/download/80XX%20Programmer_Manual(latest%20version).rar
https://tftwiki.ru/wiki/Windows-1252
https://ru.wikipedia.org/wiki/ISO_8859-1
https://ru.wikipedia.org/wiki/CP866

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


  1. xshura
    26.09.2021 23:01
    +8

    Приобрел я на днях термопринтер (кассовый аппарат, если изволите)

    На обычном "кухонном" термопринтере не напечатаешь фискальный чек. Т.е. это не кассовый аппарат.


    1. IIIarp Автор
      26.09.2021 23:30

      Согласен с замечанием, но всем известно что фискальные регистраторы делаются на основе той машинки что я держу в руках сейчас...

      Единственное отличие что эта машинка не сохраняет в памяти чеки и не пишет фразу "фискальный чек", хотя дописать ее по факту можно.

      У этого принтера довольно обширное АПИ (размеры шрифта, центрирования, начертания, изменчивый DPI, баркоды, bitmap'ы, кастомные шрифты)
      Во многом хорошее решение за малые деньги, если нет нужды в фискальном аппарате.


      1. AlexVWill
        27.09.2021 00:30
        +6

        "Единственное отличие что эта машинка не сохраняет в памяти чеки" - а это не является ли случайно ключевым критерием? Я чесслово не знаю, что-то стало любопытно.


        1. IIIarp Автор
          27.09.2021 00:47

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

          Но на основе этого принтера уже можно научится половине дела и написать эту статью.

          btw. если я уберу фразу "кассовый аппарат" из первого абзаца статьи (где оно 1 раз было использовано за все время), то останется ли еще смысл для этой ветки комментариев? :)


          1. zaiats_2k
            27.09.2021 09:20
            +8

            Фискальный регистратор имеет свой апи, с описанными вами Esc-последовательностями не имеющий ничего общего. У него свой бинарный протокол. И у него там совершенно другие абстракции, не буквы, шрифты и кодировки, а команды типа "открыть смену", "открыть чек", "добавить в чек такой-то товар", "закрыть чек", "закрыть смену", "распечатать z-отчёт".


            1. impfromliga
              27.09.2021 09:58

              К браузеру ведь любой ком теперь можно подключить,
              так что можно и настоящую кассу... единственное что для этого придется ВЕСЬ бинарный протокол на JS реализовать с нуля, это несколько вложенных подуровней со своими стандартами, от производителя, от ФНС, и от ОФД.
              Самые популярные производители его уже лет 10 пишут или больше (и не на JS конечно, а на языках более подходящих, низкого уровня)

              хотя теоретически если очень запотеть возможно )


              1. zaiats_2k
                27.09.2021 20:14

                Не знаю о каких вы уровнях. Делал несколько лет назад поддержку ККМа, когда властям взбрело в голову платёжные терминалы фискализировать. Небыло там никаких подуровней, коннектишься к виртуальному COM-порту и шлёшь команды фискальнику. А с принтером он уже сам общается.


            1. dimao79
              28.09.2021 06:13

              Все так.

              Никакого прямого доступа к принтеру в фискальном регистраторе нет, что логично и понятно.

              Как никакого отношения нет и у термопринтера к печати фискальных чеков под видом аппаратного РРО.

              Ну а если автору нужна работа с программными РРО - там не нужна печать через ESC/POS с прямым доступом к принтеру из браузера и нет нужды самому писать драйвер принтера на JS.


      1. ToSHiC
        27.09.2021 02:43
        +2

        Не знаю, как у вас, а в РФ несколько лет назад ввели штуку под названием ФН - фискальный накопитель. В каждом чеке каждая строка о товаре, количество товаров, стоимость, а так же суммарная стоимость сохраняется в нём, шифруется и отправляется в ФНС. Поэтому чек - это не просто какой-то текст, а особенным образом сформированный список. Причём он сохранится в ФН, и оттуда ничего нельзя удалить, а данные в нём должны совпадать с бухгалтерской отчётностью. Так что для тестирования формирования настоящих чеков необходимо использовать специальный тестовый ФН.

        По ФН были ЭКЛЗ, с примерно похожей концепцией, но там сохранялась только итоговая сумма чека. Которая тоже должна совпадать с отчётностью.


        1. esaulenka
          27.09.2021 10:24
          +1

           были ЭКЛЗ

          Не совсем так. ЭКЛЗ могли сохранять весь чек. Но была "дырка" в законодательстве, что требовалось только совпадение итоговой суммы. Кроме того, памяти ЭКЛЗ довольно ограничена (при активной работе они заканчивались меньше чем за год, а замена стоила заметных денег), а интерфейса обмена касса-эклз весьма тормозной.

          В итоге, все использовали только одну команду "покупка" на всю сумму сразу (касса при этом печатает "лишнюю" строчку с суммой перед строкой "итого"), а все остальные строки печатались обычным текстом, и ни в какие ЭКЛЗ не попадали.


          1. jo_b1ack
            28.09.2021 13:52

            ну у ФН срок тоже не бесконечный, 13-15 месяцев


      1. esaulenka
        27.09.2021 10:36
        +2

        но всем известно что фискальные регистраторы делаются на основе той машинки что я держу в руках сейчас...

        Всем известно, что фискальные регистраторы делаются методом выкидывания из этой машинки всех мозгов, и заменой их на свои, с возможностью подключения внешней штуковины для сохранения чеков (Не знаю, как у вас в стране это устроено. Наверняка, как и у нас в РФ - память для чеков делается отдельной конторой, которая имеет особо близкое знакомство с законодателями и имеет кучку бумажек от заведения, занимающегося криптографией) и полностью своим протоколом.


      1. goodic
        27.09.2021 10:46
        +1

        Ну как бы отличий там побольше. И ключевое - протокол. Фискальники в ESC крайне редко умеют. А поддержать их собственный протокол уже не так и просто.


      1. dimao79
        27.09.2021 12:02

        Вы путаете термопринтер и фискальный регистратор, который кроме "печати чеков" имеет много других функций, как то: сохранение всех чеков в фискальной памяти, подведение дневных итогов с печатью X- и Z-отчетов, отправку всех этих данных в налоговую, печать сохраненной информации по чекам и отчетам из фискальной памяти и много всего другого.

        Если Вам надо "печатать чеки" из программных РРО - то просто установите этот принтер как обычный принтер в системе и печатайте из браузера без всех извращений в виде "8 битных слов", ручной перекодировки UTF-8 в однобитные кодировки принтера и ковыряния ESC последовательностей. Тем более, что Вам на это даже Винда намекнула прямым текстом, заблокировав низкоуровневый доступ к этому принтеру.


      1. jo_b1ack
        28.09.2021 13:50
        +1

        >>Единственное отличие что эта машинка не сохраняет в памяти чеки и не пишет фразу "фискальный чек", хотя дописать ее по факту можно.

        Да нет... это не единсвтенно отличие... самое основное что ккт должен быть реестре сертифицированного оборудования РФ


  1. irbis_al
    26.09.2021 23:07
    +5

    Не знаю зачем Вы так натягивали сову на глобус...Обычно работа с переферией (POS принтеры, Фискальные регистраторы, Сканеры штрихкода Rs-232) Чтобы соеденить с браузером,то делается это через WebSoket.

    В Вашем случае.

    1.Устанавливаем драйвер.(Неважно Linux или винда)

    2.Дальше надо разработать шлюз(некую програмку (у меня есть и на node и java))...принимает пакет данных через websocket (xml или json) и (у меня преобразует по некому шаблону разметки(в xml) в формат pdf и печатает...можете печатать по низкоуровневуму протоколу принтера из этого шлюза).

    3.Из браузера типа var socket = new WebSocket("ws://localhost:7081");

    socket.send("HELLO");Прога шлюз прослушивает порт и выполняет задачу.

    Так можно и отправить на фискальный регистратор подключенный к rs-232.И от сканера штрихкода(rs-232) принять данные.(в любой операционке)


    1. IIIarp Автор
      26.09.2021 23:14

      Так не интересно :)
      Суть была скорее в том что-бы обработать это все по USB.
      Ваш комментарий вполне уместен, у данного аппарата естественно есть порт интернета и в случае подключения его через интернет, прямого доступа к нему с веб страницы не будет и в ход пойдет шлюз описанный вами.


      1. irbis_al
        27.09.2021 00:08

        Началось с того ,что жене аппарат в магазин :-) Если это не концептуальный,а реальный проект,то...Запаритесь Вы методом по usb из браузера такие чеки печатать.(Настоящие по ссылкам(Может в Украине доступны не будут ссылки,найдете способ как открыть))

        https://cloud.mail.ru/public/oR3A/8sQfcycUe

        https://cloud.mail.ru/public/xzLB/zXAK8t6ss

        А WS(Тут ниже обсуждалось) тут нужен именно для того чтобы разрулить момент ...сайт грузится с одного URL, а аппарат находится локально.(или у Вас много касс и на каждой локальное оборудование,в Этом случае ws://localhost: Спасает ибо WS может "ломится на" любой хост (http post на localhost сделать будет проблематично если сайт загружен не с localhost) )

        А еще я так понял у Вас Украина(Ибо в России запрещены такие аппараты для печати чека..нужно использовать только Фискальные Регистраторы(ФР))...Это значит со временем Вы будите использовать ПРРО(Программный РРО) и QR оттиск печатать.и тут уже точно без промежуточного локального шлюза будет непросто.(ПРРО тоже могу поделится :-) )


        1. IIIarp Автор
          27.09.2021 00:33

          Спасибо большое :)
          По этой теме все что-бы вы не показали будет мне интересно.

          Проект концептуально/реальный, смотря на сколько хватит терпения в разработке.
          Возможно я да раскошелюсь на какую-нибудь систему из существующих. (УкрСклад вроде не дорого стоит, если то цена единоразовая -_-)
          Если все будет подключатся через USB то это вполне меня устроит, касса одна на магазин, если было бы несколько, то все равно было бы уместно для каждой кассы иметь свой аппарат с подключением на прямую, не бегать же с одного конца помещения в другой за чеком.

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

          Если что выйдет, будем пользоваться, не успею так куплю готовое решение.


        1. ddidwyll
          27.09.2021 00:35

          http post на localhost сделать будет проблематично если сайт загружен не с localhost

          Не сталкивался с такой проблемой, разве

          fetch("http://localhost/endpoint", params)

          не сработает?


          1. irbis_al
            27.09.2021 09:02

            У Меня не срабатывало...Дело в том что ,бразузеры используют Same Origin Policy

            https://www.w3.org/Security/wiki/Same_Origin_Policy

            POST и GET Должны делаться на тот же IP адрес с какого загружена страница.(В каких то браузерах Где-то это можно в настройках исключить...а где то вообще нельзя(или я не знаю как)...как бы там ни было разработчикам лучше придерживаться стандартного поведения браузеров,а не подавлять настройками их дефолтное поведение)


            1. VolodjaT
              27.09.2021 10:13

              Ну так погуглите "how setup cors [framework name]".


            1. mayorovp
              27.09.2021 14:16

              SOP тут ни при чём, за запросы отвечает CORS.


              Но даже он не обязателен, если указать в параметрах mode: 'no-cors'


    1. ddidwyll
      26.09.2021 23:26
      +1

      делается это через WebSoket

      Зачем тут ws, чем вам обычный http(s) не угодил? Понятно что отправлять данные можно как угодно, но ваш совет могут прочитать начинающие и решить что в данной задаче ws необходим, что неверно.


      1. IIIarp Автор
        26.09.2021 23:41

        С вами тоже согласен :)

        Почему мне хотелось обработать этот принтер по USB а не по интернет порту, потому-что хотелось иметь возможность подключить к пк любой принтер и сразу начать печать.

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

        Иначе, в случае изменения адреса/порта, придется менять настройки и в приложении + нужно иметь провод и тащить его от кассы к роутеру, а так все будет под рукой и переносимо.

        Не говорю что это лучшее решение, но наиболее удобное для меня.


    1. victorZX
      26.09.2021 23:52

      А как Java отправляет данные на принтер? Есть какая-то готовая библиотека для этого?


      1. pewpew
        27.09.2021 00:44

        Так статья про JavaScript и браузер.


        1. victorZX
          27.09.2021 10:20

          Вот это я имел ввиду: 2.Дальше надо разработать шлюз(некую програмку (у меня есть и на node и java))...принимает пакет данных через websocket (xml или json)


      1. victorZX
        27.09.2021 10:19

        Так я понел. Просто тут кто-то сказал, что реализовал на джаве интерфейс роботы с апаратом


        1. irbis_al
          27.09.2021 10:32

          Почти верно...сама идея в том что на компе клиента запущенаи прослушка(listener) WebSocketа ,и в зависимости какая прослушка запущена по порту, то и работает.

          В данном случае получает данные из бразузера формирует чек формата pdf и тут же его печатает.А можно подключить маркеровочное оборудование .Весы.и т.д.(Я говорю не про концептуальную модель,а про реально работающюю модель из браузера подключаются разные "железяки")


    1. crackedmind
      28.09.2021 14:58

      Ну если уж на то пошло, то в хроме (ну и в edge) с июня месяца Web Serial доступен без всяких экспериментальных флагов.


  1. victorZX
    26.09.2021 23:15

    Спасибо автору. Вдохновился и себе купить такой принтер, просто, чтобы поиграться вот так, прямо из хрома. Однако быстрое гугление ничего не нашло по марке ‘thermal receipt printer’. На понятно, где купить такую железку


    1. xshura
      26.09.2021 23:16
      +1

      Их на Али как грязи.


    1. IIIarp Автор
      26.09.2021 23:19

      Спасибо за отзыв :)
      Купить можно по фразе POS printer
      https://prom.ua/Pos-5890.html

      P.S. Что-бы прям вот так, смотрите в описании что-бы подключение через USB было доступно.


      1. victorZX
        26.09.2021 23:55

        1. IIIarp Автор
          27.09.2021 00:08

          Очень похож, такой же, без каких-либо обозначений на самом принтере, но в моем, кстати, нет ребристых боковин и верхняя крышка немного утоплена по бокам.


  1. Radish
    26.09.2021 23:56
    +3

    Как результат кода свыше, русских букв я так и не увидел...

    Этот код дан мне Господом!


    1. IIIarp Автор
      27.09.2021 00:41

      Исправился :)


    1. qwertyqwerty
      27.09.2021 13:28

      Это действительно код свыше.

      https://youtu.be/RirqnBUQTEU


    1. vriska
      06.10.2021 14:37

      image


  1. Aleksandr-JS-Developer
    27.09.2021 00:06
    +1

    Даёшь библиотеку (а лучше новый фреймворк) на github


  1. serafims
    27.09.2021 00:36
    +1

    Не рекомендую такой подход - сдохнет принтер - и придется с новым развлекаться аналогично. По идее лучше через стандартные драйвера печати и программу-шлюз. Но работа напрямую с принтером иногда в разы быстрее выходит.


    1. Vasily_Pechersky
      27.09.2021 12:25

      В термо/наклеечных принтерах есть распространённый стандарт ZPL и другие. Вот фреймворк javascript->zpl будет по сердцу многим.


      1. dimao79
        27.09.2021 13:49
        +1

        ZPL в Зебрах, у автора ESC/POS.


    1. ignat99
      27.09.2021 21:44

      Про Zebra GD420 и ZPL под CUPS. На C++ немного быстрее работает:

      Все «тайны» настройки софта для плагинов и периферии OpenCPN / Хабр (habr.com)


  1. elfukado
    27.09.2021 08:43

    А в чём смысл?

    ККМ ведь подключают к товароучётной системе, она и будет чеки печатать.

    Или вы и аналог 1С будете делать и данные в налоговую отправлять?

    А так да, интересная штука, на работе раньше на таком пароли и прочие мелочи выдавали.


    1. FedorovDimulya
      27.09.2021 17:57

      Мне кажется, что это больше развлечение для него было


      1. elfukado
        27.09.2021 18:07

        Зачем гадать, если у него в первом предложении написано, что хочет сделать свою web-кассу?


  1. belav
    27.09.2021 09:23

    А не проще сформировать bitmap, там написать текст на любом языке и отправить на печать? Зачем заморачиваться со встроенными шрифтами?


    1. victorZX
      27.09.2021 10:24

      Тоже об этом подумал. Тогда рисовать можно каким угодно шрифтом, заморочиться логотипом, например. Хочу купить себе такой же свисток и поиграться


    1. klirichek
      27.09.2021 12:05

      Собственно, в CUPS (linux/apple) именно так и сделано.

      Получился неплохой мелкий пет-проект (zj58 на гитхабе)


    1. berez
      27.09.2021 13:21

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

      image


      1. esaulenka
        27.09.2021 13:39
        +2

        Или могут «оптимизировать» строки, где много однотонной заливки — сделать их светлее

        Это не оптимизация, и с битмапами никак не связано. Это физика и кривые руки разработчика принтера. Штука в том, что тёмная точка на бумаге получается, если эту бумагу нагреть. Самый простой и быстрый способ печати - греть сразу все нужные точки в текущей печатаемой линии. Но если нужны сразу ВСЕ точки, происходит беда - на это банально не хватает электричества, печать становится бледной.

        Решения: 1) считать чёрные точки, если их много, по некоей эмпирической формуле увеличивать время нагрева 2) делить область печати на несколько частей, печатать по-очереди 3) мерять напряжение и опять-таки увеличивать время при необходимости.

        Правда, надо заметить, идеального результата у меня достигнуть не удалось...


        1. berez
          27.09.2021 18:01

          Это физика и кривые руки разработчика принтера.

          С кривыми руками соглашусь, с физикой — тоже, но не полностью. :)
          Лично мой китаепринтер иногда даже пустые строки начинает вставлять в особо «бледных» местах. В результате картинка вся в разрывах и растягивается на полметра ленты. Т.е. разработчики что-то там пытались оптимизировать, да не осилили.


  1. belch84
    27.09.2021 12:33

    Как раз сейчас разбираюсь с такого типа принтером для его использования в сочетании с кассовой программой. Модель принтера DATECS FP-101 Smart (так он называется в Украине). Старый вариант кассовой программы работал с подобными кассовыми регистраторами по протоколу DATECS, но в техподдержке рекомендовали для использования более новый протокол Krypton. Интерфейс взаимодействия я решил оставить таким, какой поддерживался старой программой, т.е. через последовательный порт. Печать относительно легко осуществляется через стандартный OPOS-объект из любой программы, которая поддерживает объектную модель приложений Microsoft. Среди прочего, этот объект поддерживает прямое исполнение команд протокола Krypton. Все это в основном предназначено для печати фискальных документов, но можно печатать и просто тексты (метод OPOS-объекта PrintNormal), до 40-ка символов в строке. Возможно, взаимодействие с объектом можно организовать и в программе на JavaScript в браузере.

    Фискальный принтер
    image


  1. Zibx
    27.09.2021 14:32

    Реализовывали протокол термопринтера Custom на js через RS-232. Но не через браузер, а node.js. В качестве устройства использовали Qotom с кучей сом-портов. То ещё развлечение с русской кодировкой было, но задача решаемая в отличии от устранения наводок на добротный ёмкостный тач-скрин.


  1. 402d
    27.09.2021 15:17
    +1

    ГЛАВНОЕ . НЕ ПУТАЙТЕ ФИСКАЛЬНЫЕ РЕГИСТРАТОРЫ И КУХОННЫЕ ПРИНТЕРЫ.

    Протоколы которых не имеют ничего общего.

    Если там, чтото типа Атолловских, то это полная жопа. Все должно передаваться с выдержкой до миллисекунд. Никто насколько я знаю не решился переписать их страшную dll.

    Первое. USB в хроме заброшен. Во времена надеж на ChromeOS добавили.

    Второе, апи как вы называете это ESC/POS command protocol.

    Во времена войны производителей наплодили подвариантов. Китай в основном два диалекта (базовых микропрошивок) где русский язык (866) на 7 или 17 кодепейдже. Полный список извращений свыше 20 вариантов для 866 и 1251. Больше воевали с командами графики. Наиболее частые GS v 0 и ESC *

    Третья проблема сама термоголовка. Максимальное количество точек.

    384 и 576 соответственно лидеры популярности.

    Четвертая проблема обратная связь от устройства и отсутвие механизмов контроля целостности данных. Легко переполнить буфер, которые тоже различаются.

    Посылаешь медленно, начинает полосить. Так как головка еще не разогрелась.

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


    1. zaiats_2k
      27.09.2021 20:22

      > Все должно передаваться с выдержкой до миллисекунд. Никто насколько я знаю не решился переписать их страшную dll.

      Даже в голову не пришло использовать их страшную DLL. Благо у них хорошо документированный COM-протокол. Пара несоответствий в блок-схеме нашлось, но ничего критичного. Наваял на C# и отладил за пару месяцев, нормально колошматило на нескольких сотнях платёжных терминалов, пока эту дичь с фискализацией оных не отменили. ;)


      1. sandworm
        28.09.2021 09:47

        Протоколы для второй и пятой фирмвари несколько разные. Вторая делается, пятая... Я не осилил.


    1. esaulenka
      27.09.2021 22:49

      Если там, чтото типа Атолловских, то это полная жопа. Все должно передаваться с выдержкой до миллисекунд.

      Что-то страшное вы рассказываете. Там довольно наркоманский протокол с кучей переподтверждений, но он как работал 20 лет назад на гробиках с двумя лентами на убогих 51 контроллерах, так и с минимальными изменениями работает на супер передовых онлайн кассах. Задержки там, соответственно, прокатывают +- километр.


      1. 402d
        27.09.2021 23:33

        https://partner.atol.ru/files/dc/217/Protokol_KKM_2.4_040614.pdf

        меня напугали диаграмы на страницах 19 и 20. Так как одновременно разбирался как эмулируется протол COM порта под андроидом . Готовые либы вроде бы есть, но одни растут из гугловского кода и поддерживают не все, а другие в основе имеет GPL лицензию. А родить аналог готовых либ, который через булк будет правильно работать . Увы мне слабо.


        1. esaulenka
          29.09.2021 16:06

          Ох, преданья старины глубокой...

          На самом деле, можно просто сделать тупой протокол, который будет работать по success path (там парой страниц выше расписано), а потом, для обработки ошибок, просто начинать всё заново, если не удалось отправить (получить ack на команду). Главное, не отправлять команду, если пришёл на неё ack (вы же не хотели получить две одинаковые продажи? :-) ), остальное протокол со стороны железки нормально съест.

          Про миллисекунды там тоже ничего не написано, совершенно лошадиные таймауты (минимум 0.5 сек). Я, правда, совершенно не в курсе работы с ком-портами под андроидом. Там совсем всё плохо, и к стандартным /dev/ttyXXX не пускают?


          1. 402d
            29.09.2021 17:05

            Начнем с того, что моделей телефонов много. USB-OTG поддерживают не все. Очень часто не получается программно определить поддерживает или нет. Все методы говорят да, а фактически не работает. Единственный простой надежный способ - подключить USB мышку - появился курсор - ок.

            Во вторых надеятся можно только на низковольную версию usb порта (150мв) . А вот устройства почему то проектируют из расчета 500 мВ . ;)

            В третьих начудили с безопастностью. Каждое подключение - новый идентификатор, к которому нужно получать от пользователя явное разрешение. Тут есть обходной путь. Ловить все подключения устройств, тогда пользователь может проассицировать принтер с твоей программой.

            Версий андроида много, да еще производители свои кастомы добавляют. Так, что /dev/ttyXXX как вилами на воде. Может будет, но вероятнее всего, что нет.

            В общем в андроиде USB сделано по остаточному принципу. Те же клавиатуры внешние до сих пор через одно место работают. Да и в программах не удобно реализовывать поддержку. в androidX материал компонентах поломали клавиатурную навигацию.


            1. esaulenka
              30.09.2021 11:09

              На всякий случай сообщу, что ток измеряется в миллиамперах, а не в милливольтах (и вольт до появления power delivery там всегда было 5, плюс-минус отклонения китайских реализаций).

              Термопринтер же жрёт при печати полсотни ватт, и питание от USB там никак не помогает - оно просто используется для определения факта подключения кабеля, потребление от USB - доли мА.


              1. 402d
                30.09.2021 11:37

                Вы правы, USB работает на 5 вольтах. Почему то описался с силой токой. Теоретически да термопринтер должен работать с миливольтами, но на практике андроид смартфон часто не пробивает шнурок или цепочку адаптеров, кабелей .

                Поддержкой USB заморчиваются в основном производители SDK для своих устройств. Простые программисты реализуют сетевой протокол на 9100 и работу через классический блютуз SPP. Программно это работа с потоком с небольшой оберткой на подключение. Ну или используют готовые SDK.

                Вот только от китайских сдк впечатление, что их писали копипастой друг у друга и со стек оверфлоу.

                Глюки один в один.


  1. Aidar87
    27.09.2021 17:59
    +1

    У нас тоже чековый принтер, но мы печатаем тупо через window.open, передаём вёрстку со штрихкодом и тп и window.print()


  1. belch84
    28.09.2021 09:59
    +1

    Тестовая печать на подобном принтере
    image


  1. vetal1977
    28.09.2021 16:25

    OMG, автор - Русских букоф ведь нет - есть кириллица.


  1. AmirKamolov
    29.09.2021 17:22

    Отличная клавиатура! Тихая с низким ходом)