Переделка ККМ Атол в термопринтер

Некоторое время назад я увлекался тем, то писал самодельные прошивки для различных готовых устройств. Так, например, сделал пульт для управления солярием из пульта от охранной сигнализации. А что, смотрите сами:

  • корпус есть отличный;

  • уже встроена клавиатура и не просто, а более-менее надежная;

  • есть светодиодные индикаторы и динамик (пищалка);

  • корпус штатно крепится к стене;

  • все собрано красиво и на вид надежно.

Внутри есть платка с AVR микроконтроллером, разъемом внутрисхемного программирования. Что осталось:

  • вывести наружу com порт для подключения к серверу;

  • приделать реле для включения пускателя солярия;

  • подключить блок питания;

  • и главное — написать саму прошивку.

Рисунок 1 Самодельный пульт управления соляриемРисунок 1 Самодельный пульт управления солярием

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

Рисунок 2 Снятый с учета ФР переделанный в термопринтерРисунок 2 Снятый с учета ФР переделанный в термопринтер

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

  • печатать на разные устройства;

  • работать с 1 и 2 мерными штрих кодами;

  • уметь печатать кучей различных шрифтов;

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

  • должна быть возможность работы в режиме командной строки (т.е. программа на 1с готовит заранее текстовый файл, потом дает команду на печать);

  • показывать на экране как будет выглядеть документ после печати;

  • уметь работать со шрифтами двойной ширины/высоты, жирными, подчеркнутыми и т.п.;

  • двигать бумагу вперед и назад на любые требуемые интервалы;

  • и еще все что потребуется.

Рисунок 3 Основное окно программы печатиРисунок 3 Основное окно программы печатиРисунок 4 Редактор шрифтовРисунок 4 Редактор шрифтов

Постепенно я создал эту программу, и она уже умеет очень много всего, но вот я подхожу к основной теме статьи. Дело в том, что мне попадались на работе, у заказчиков аппараты АТОЛ. Обычно они назывались Fprint-22, Fprint-55 и еще куча вариантов. Это были совершенно разные ккм и общая черта была только та, что все они работали через драйвера Атол. В аппаратах были разные микроконтроллеры, разные термоголовки, и ширина бумаги была от 45 до 80 мм. Но все они работали через атоловские драйвера.

И часто заказчики хотели помимо основных функций (печать чеков) сэкономить на покупке термопринтера и использовать уже имеющуюся кассу для печати различных не фискальных документов. Это могли быть счета в барах, временные пароли для Wi‑Fi, талоны для въезда на территорию, те же коды для включения солярия на 5 или 10 мин. И я понял, что пришла пора для еще одной модернизации программы. Естественно, так просто на кассе не попечатаешь — это же не принтер.

Рисунок 5 Аппарат FPrint-22 печатает из моей программыРисунок 5 Аппарат FPrint-22 печатает из моей программыРисунок 6 Аппарат FPrint-55 печатает из моей программыРисунок 6 Аппарат FPrint-55 печатает из моей программы

Для начала я выделил три основных ветки драйверов. Это была 6…8 версии, 9 ая версия и 10 ая. Все они имели свои особенности.

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

Рисунок 7 Переключение версий драйверовРисунок 7 Переключение версий драйверов

6…8 версии

Тут как раз оказалось все просто. Сначала объявляем переменную

IFprnM45                   ECR;

 Потом создаем объект:

COleException *e = new COleException;

try

{

       if (!ECR.CreateDispatch("AddIn.FprnM45", e))

             throw e;

}

Дальше включаем устройство:

ECR.SetDeviceEnabled(true);

И готово, можно делать с кассой все что хочешь. Например,

ECR.FullCut();

Это полная отрезка ленты. В конце вызываем

ECR.ReleaseDispatch();

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

Потом к проекту на с++ я добавлял файл с заголовками функций fprnm1c.h.

Рисунок 8 Тест драйвера 6ой версииРисунок 8 Тест драйвера 6ой версии

10 версия

Для нее я уже не смог повторить тот фокус что для 6ой. Файл с заголовками я нашел, а дальше был затык. Может быть конечно я что‑то не допер, но создать объект fptr10 у меня не получилось. Пришлось подключать на лету dll от 10ой версии fptr10.dll и извлекать из нее функции. Делал я это так:

hDll10 = LoadLibraryA("fptr10.dll");

// загружаем

typedef int (WINAPI *PFN_libfptr_create)(libfptr_handle);

PFN_libfptr_create p_libfptr_create;

// описываю импортируемую функцию

 

libfptr_handle fptr10;

// описываю дескриптор

 

p_libfptr_create = (PFN_libfptr_create)GetProcAddress(hDll10, "libfptr_create");

// получаю ссылку на функцию

 

iRes = (*p_libfptr_create)(&fptr10);

// вызываю саму функцию

Вот так удалось подружиться с 10ой версией драйвера.

Рисунок 9 Тест драйвера 10ой версииРисунок 9 Тест драйвера 10ой версии

9 версия

Это была для меня самая нужная версия. Дело в том, что разработчики драйверов применяли несколько видов протокола обмена с кассами. Я знал про 2 и 3 версии. 6ая версия драйверов работала, как я понял со 2ым протоколом, 10ые драйвера с 3им протоколом. А 9ые драйвера были переходными! И в них была возможность выбора 2 или 3 версии протокола! Это было круто, т.к. позволяло работать и со старыми кассами и с новыми. Вот тут я и получил большую часть проблем. Но в итоге все заработало. Делал так:

hDll = LoadLibraryA("fptr.dll");

// загружаем DLL

 

TED::Fptr::IFptr *fptr;

// описываю указатель на дескриптор

 

typedef TED::Fptr::IFptr * (WINAPI *PFN_CreateFptrInterface)(int);

PFN_CreateFptrInterface p_CreateFptrInterface;

// описываю импортируемую функцию

 

p_CreateFptrInterface = (PFN_CreateFptrInterface)GetProcAddress(hDll, "CreateFptrInterface");

// получаю ссылку на функцию

 

fptr = (*p_CreateFptrInterface)(i);

// вызываю саму функцию

Тут меня ждала еще одна засада. При вызове CreateFptrInterface надо было передавать переменную int. Типа версию драйверов. Я передавал: 9, 0, 1, 6, 8, 10 и вообще хрен знает что, но вызов не срабатывал: ‑(В итоге я решил сделать так. Создал цикл от 0 до 10 000 и вызывал эту заразу в цикле со всеми аргументами подряд, пока не вернет нормальное значение. И она вернула! В общем, удалось подобрать число. Если хотите, попробуйте сами подобрать, пишите в коменты что получится.

После этой инициализации можно уже было вызывать разные методы. Но тут встал главный вопрос:

Как печатать?

К драйверу‑то я подключился. Методы вызывались, а дальше?! Как вывести на кассу что‑то нужное, и при этом ее не убить? Для решения этого вопроса, я очень внимательно прочитал руководство программиста по драйверу атол и понял, что единственный шанс — это печатать документ как картинку. Моя программа как раз умела сохранять результат работы в формате BMP, и полдела уже было сделано. Далее я добавил команду для печати уже через драйвер атол и при нажатии соответствующей кнопки сначала формировал BMP файл (конечно же монохром 1 бит на пиксель) и потом уже начинал манипуляции с драйвером. Выглядели они примерно так:

ECR.SetFileName("c:\\tmpbmp.bmp");

ECR.PrintBitMapFromFile();

Рисунок 10 Тест драйвера 9ой версииРисунок 10 Тест драйвера 9ой версии

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

Также были сложности с шириной и высотой картинки. Разные аппараты имели разные размеры печатной области. Приходилось подстраиваться.

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

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

В общем, программа заработала, несколько аппаратов из имеющегося у меня хлама вообще не смогли ничего распечатать, но это были совсем старые модели, которые имели 2 ленты и ширину бумаги 45 мм. У них просто не было вообще команды печать графики.

© Habrahabr.ru