Маркировка: Отправка RAW-команд на принтер (EZPL, PCL)

Проекту исполнилось 15 лет! Поддержать проект материально, проспонсировать проекты Автора или сделать ему подарок можно на этой странице: "Донаты и Спонсорство, Список Желаний".

Число просмотров: 9 889 

Итак, я написал уже два поста про EZPL и то, как я его применяю у себя в 1Ске для печати на принтере Godex G500. Но я не раскрыл (а точнее, раскрыл не до конца) самый главный секрет, над которым, как оказалось, народ на форумах бьётся много лет. Проблема в том, что раньше, когда маркировочные принтеры подключались по COM- или LPT-портам, очень просто можно было заслать на такой порт текстовый файл команд. Достаточно было написать что-то типа «Copy Commands.txt COM2», и система открывала COM-порт как файл и копировала туда всё, что нам надо. Но сейчас большинство маркировочных принтеров (да и принтеров вообще) подключается по USB-портам, а некоторые вообще по LAN. И что делать? В консоли «Copy Commands.txt \\Printer» не напишешь — система не поймёт тебя. И вот народ мучается, например получая такие вот ответы на форуме поддержки Godex (http://www.scancode.ru/forum/topic/6/6/):

Скриншот сообщения из форума, где предлагается написать программку

Скриншот сообщения из форума, где предлагается написать программку

На их форум я вышел по поисковым запросам «Отправка RAW-команд на принтер» или «Печать на EZPL по USB/LAN». Эти запросы я специально оставил в тексте поста, потому что хочу чтобы моя утилитка людям, которые перекопали весь инет в поисках простого решения, пригодилась.

…но мы, однако, возвращаемся к исходному вопросу. Вот есть у меня принтер Godex G500, который имеет LAN-порт. И изучил я язык EZPL так, что хочу рисовать на этом принтере этикетки напрямую, без обработчика WinPrint и мутных настроек драйверов. ЧТО ДЕЛАТЬ?!

Как оказалось, есть два рабочих варианта: использовать специально написанные для 1С компоненты или работать с COM-портами как и раньше (например купить конвертер LAN <> RS-232). Но с компонентами для 1С тоже есть проблемы: во-первых, они только для 1С (а мы может хотим из какого-нибудь скрипта печатать), во-вторых, часть из них платные, а в-третьих, ещё часть из них жутко ограничены в настройках. Например на Scancode.ru лежит компонента для 1С 7.7/8 (в разделе «Файлы»), но она не умеет рисовать QR-коды, а только EAN-13. А это мне не подходило.

Ну так и если ничего не подходит — может быть стоит написать? И тут я вспомнил свои программерские будни на VC++, поднял доки по Windows API и сделал такую программку, как и хотел. Оказалось, что всё-всё просто до ужаса. В Windows API есть штатная функция OpenPrinter(), которая получает… обычное текстовое имя принтера точно так, как он в папке «Принтеры» называется. И ей пофигу, находится ли этот принтер удалённо или нет и по какому порту он подключен. А после того, как мы принтер открыли — достаточно пробежаться по функциям типа StartDocPrinter(), StartPagePrinter(), WritePrinter(). Сам кусок кода с вырезанными проверками ошибок выглядит примерно так:

Исходный код утилиты RAWCmd

Исходный код утилиты RAWCmd

После этого мне понадобилось поразбираться с кодировками в консоли, чтобы все данные верно брались из параметров и передавались без искажений, и после этого у меня получился простенький готовый продукт. Вот как это всё выглядит:

Утилита RAWCommans для отправки RAW-команд на принтеры напрямую

Утилита RAWCommans для отправки RAW-команд на принтеры напрямую

Это обычная консольная утилитка. Все нужные библиотеки, кроме системных, интегрированы внутрь неё, так что она переносима и ничего лишнего не требует. У меня она работает на WinXP, и должна работать и на более старших виндах. Утилитка делает только одну вещь: открывает принтер по имени, создаёт задание печати с указанным именем и отправляет на принтер указанный файл. Поэтому утилита годится для ЛЮБОГО принтера маркировки с любым языком: никакие специальные команды принтеру она не шлёт, тупо отсылая содержимое файла. А что вы там в файле напишете — то на принтер и пойдёт.

Запускается она так: RAWCmd.exe «Название принтера» «Название задания» «Файл команд». Если параметры содержат пробелы — то их надо указать в кавычках. Например если принтер зовётся MyG500 — то указываем его напрямую. А если он зовётся Godex G500 — то надо указать в кавычках «Godex G500». У программки на каждый чих есть куча кодов возврата, так что все ошибки можно будет отследить в bat-файлах и других скриптах, если потребуется.

Я создал файлик из одной буквы «E» (конец команд для Godex) и запустил утилиту, чтобы показать как это отрабатывает:

Отправка тестового задания печати RAW на принтер

Отправка тестового задания печати RAW на принтер

В итоге в очереди печати принтера создалось задание с нашим названием, которое содержит данные размером в три байта (один байт — буква «E» и два байта на конец и перевод строки).

Задание печати, созданное утилитой RAWCommands

Задание печати, созданное утилитой RAWCommands

Утилита лежит вот по этому адресу: CS-RAWCmd.rar (~60 кб). Пользуйтесь на здоровье, только сохраняйте мои копирайты.

Проекту исполнилось 15 лет! Поддержать проект материально, проспонсировать проекты Автора или сделать ему подарок можно на этой странице: "Донаты и Спонсорство, Список Желаний".

20 Отзывов на “Маркировка: Отправка RAW-команд на принтер (EZPL, PCL)”


  • 1 fiber  [СПб]

    А давайте ему памятник поставим? Ну маленький хотябы.. :)

  • 2 CS  [Москва]

    Кому — ему? Мне не надо =)

  • 3 garp

    В похожей ситуации с usb-принтером, правда обычным, офисным, я поступил следующим образом — расшарил принтер, написал в командной строке net use \имя_компа\имя_расшаренного_принтера LPT1.
    Прокатило.

  • 4 CS  [Москва]

    Ну вот так все и предлагают по форумам делать. Но это колхоз, потому что лишняя шара появляется и так ещё по сетке не попечатаешь с разных компов.
    Поэтому я вот и написал эту хреновину =)

  • 5 garp

    Такой себе колхоз. Шара — тож не беда, чай не сетка ФСБ -). А на соседний комп с неменьшим успехом можно поставить дрова, расшарить принтер и т.д.

    Ой, да даже и драйверы ставить не надо. Подключиться просто к принтеру с соседнеог компа, сказать net use, да и все.

  • 6 CS  [Москва]

    Так мы разве спорим? Мне тот способ не нравится — это лишние настройки. Я хочу добиваться того, чтобы поставил принтер, дрова — и всё. Больше никаких херовин. А net use кажется при перезагрузке уже слетает.

  • 7 garp

    Нет, не спорим, конечно. Что до net use, то он сохраняется по пользователю после перезагрузки. Если пользователей ходит несколько, то можно применить очевидный колхозный метод — прописать net use в автозагрузку.

  • 8 CS  [Москва]

    Ага, прения устранены. Я его никогда не юзал и не знал, сохраняется ли подключени… а, хотя где-то ж была галка типа «Восстанавливать сетевые диски при входе в систему».

  • 9 MARAT

    Здравствуйте, три ваши статьи в блоге здорово облегчили мне настройку Codex G330, благодарю вас! У меня есть ещё вопрос, мне нужно из 1С вызывать печать этикеток с заданными значениями, которые формируются из номенклатуры. Номенклатуры много и она в таблице. То есть мне нужно в 1С запустить цикл, где создавалась бы структура с кодом EZPL для принтера и сразу отправлялась бы на печать. Не могли бы вы привести кусок кода из вашей пробной программки, где реализуется отправка файла/структуры в принтер. Как загрузить изображение и создать код в GoLabel я уже разобрался. 1С Предприятие 8.3, Codex G330 по USB.

  • 10 CS  [Москва]

    Привет! УРА! Вот кому-то сгодилось. А ты с 1Ской как? На ты или нет? Там на самом деле всё тупо. И у меня семёрка, так что код будет для неё. По идее так:
    1. Номенклатура у тебя отбирается запросом же? То есть, воткнуть цикл, как в семёрке было .ВыбратьСтроки() и .ПолучитьСтроку() ты сможешь?
    2. Вот у тебя в цикле будет перебираться твоя номенклатура. Положим, ты её загонишь в переменную ТекТовар.
    3. Дальше ты берёшь и всё, что GoLabel нам дала, соханяешь в обычный текстовый файл. Можешь положить его например в папку КаталогИБ() + «Templates» какие-нибудь.
    Туда же можно положить мою утилитку, чтобы она под рукой валялась и было известно где.
    4. В этом текстовом файле ты САМ придумываешь ключи типа там $TOVAR_NAME, $TOVAR_CODE и прочие. Какие тебе нравятся.
    5. Дальше тебе до цикла надо ТУПО считать этот файл в переменную. Например в СтрШаблон. Положим, это делается так:
    ФайлШаблона = ПапкаШаблонов + "" + ТекШаблон.Файл; Если (ФС.СуществуетФайл(ФайлШаблона) = 0) Тогда   Сообщить("ОШИБКА: Не найден файл шаблона """ + ФайлШаблона + """. Этикетка не будет напечатана!", "!");   Возврат; КонецЕсли; //Считаем файл и загрузим его построчно в переменную, вырезая строки с комментариями СтрШаблон = ""; ТШаблон = СоздатьОбъект("Текст"); ТШаблон.Открыть(ФайлШаблона); КолСтрок = ТШаблон.КоличествоСтрок(); Для ТекНомСтр = 1 По КолСтрок Цикл   ТекСтр = ТШаблон.ПолучитьСтроку(ТекНомСтр);   Если (Лев(ТекСтр, 1) = ";") Тогда     ТекСтр = "";     Продолжить;   КонецЕсли;   //Прибавляем любые строки, даже пустые, чтобы не терять командные штуковины   //потому что некоторые команды могут иметь и пустые строки   СтрШаблон = СтрШаблон + ТекСтр + РазделительСтрок; КонецЦикла;
    6. А теперь тебе остаётся взять цикл по товарам и ТУПО заменить через выражение типа СтрШаблон = СтрЗаменить(СтрШаблон, «$TOVAR_NAME», ТекТовар.Наименование); Логично же? =) Работаем с текстовыми переменными.
    7. АХТУНГ!! Я ТУТ КОСЯКНУЛ, но ты разбеёшься же? Наш СтрШаблон надо будет один раз брать из файла, а потом копировать его в другую переменную, с которой и работать уже. А для каждого следующего товара в цикле снова брать СтрШаблон и менять его $xxx на значения из ТекТовар.
    8. Если надо несколько копий этикетки — то просто тупо проделываешь то же ещё раз. То есть, я не принтеру копии задаю, а просто высираю несколько раз одни и те же команды.
    9. После этого всего ты создаёшь где хочешь файл и сохраняешь эту переменную туда, как обычно, когда нам надо текст в файл пихнуть:
    ФайлКоманд = КаталогВременныхФайлов() + "raw-labels.pdb"; ТШаблон.Очистить(); //Добавим строку столько раз, сколько копий указано ТекКопий = ТекШаблон.Копий; Если (ТипПечати = "Т") Тогда   ТекКопий = 1; КонецЕсли; Для ТекСтр = 1 По ТекКопий Цикл   ТШаблон.ДобавитьСтроку(СтрШаблон); КонецЦикла; ТШаблон.Записать(ФайлКоманд);
    10. Теперь мы имеем файлик ФайлКоманд, в котором у нас лежит всё что надо высрать на принтер. Дальше тебе надо только составить командную строку для моей утилиты и запустить её. Это делается так:
    //Теперь составим мега-команду из задания печати, программы и принтера НазваниеПринтера = СокрЛП(Константа.ПринтерЭтикетокНазвание); Если (СписПринтер.РазмерСписка() > 0) И (СписПринтер.ТекущаяСтрока() > 0) Тогда   НазваниеПринтера = СписПринтер.ПолучитьЗначение(СписПринтер.ТекущаяСтрока()); КонецЕсли;    ОбДоговор = глНайтиДокументДоговора(ТекВладелец); Если (ПустоеЗначение(ОбДоговор) = 0) Тогда   ТекНазвание = СокрЛП(ОбДоговор.ИДЗаказа) + "/" + СокрЛП(ОбДоговор.НаименованиеЗаказа); Иначе   ТекНазвание = СокрЛП(ТекВладелец.Контрагент); КонецЕсли;    НазваниеЗадания = "Щит " + СокрЛП(ТекВладелец.НомерДок) + " [" + ТекНазвание + "] - " + ТекШаблон.Название + ?(ТипПечати = "Т", " (тест)", ""); ПутьУтилиты = СтрЗаменить(СтрЗаменить(СокрЛП(Константа.ПринтерЭтикетокПутьУтилиты), "$IBDir", КаталогИБ()), "", ""); //Командная строка: //  RAWCmd.Exe <"PrinterName"> <"DocumentName"> <"CommandsFile"> //    * <"PrinterName">  - Name of printer to send commands. //    * <"DocumentName"> - Name of printer document job (for queue). //    * <"CommandsFile"> - Name of file (text) to send. // //  RAWCmd.Exe "GODEX G500" "My Label" Small-Labels.txt   СтрКоманда = """" + ПутьУтилиты + """ """ + НазваниеПринтера + """ """ + НазваниеЗадания + """ """ + ФайлКоманд + """"; ЗапуститьПриложение(СтрКоманда);
    Вот и вся хитрость! Короче, ОБЫЧНАЯ работа с текстовыми файлами типа «Считайте текст из одного файла, замените там символы, запишите в другой файл столько раз, сколько копий для печати надо» и потом «Составьте командную строку из разных хреней и запустите её на выполнение».
    Ещё я тут в путях ВЕЗДЕ ставлю кавычки. То есть командная строка будет выглядеть так
    «C:Program FilesTest DBDataRAWCmd.exe» «Godex G500» «Тестовое задание на печать» «C:WindowsTempraw-labels.pdb»
    Кавычки я ставлю для того, чтобы винда могла строку с пробелами распознать как одино значение. Обычно она по пробелам разделяет разные аргументы.
    И ещё! Движок сайта сейчас переставил тут кавычки на угловые. Ты это дело не забудь поправить, если код копировать будешь: нам в программировании нужны обычные кавычки, которые на русском идут как Shift+2.

  • 11 MARAT

    Я с 1С к счастью на ты:) скачал с инфостата компоненту, которая послыает данные непосредственно в порт принтера, можно отсылать даже со строки с данными, я в эту строку и копирую все переменные от базы 1с. Большое спасибо за подробный ответ!Могу сбросить компоненты на почту, если нужно:) правда они под 1С 8.2.
    Единственная проблема возникла с кодировкой текста, и тут я вспомнил вашу статью, то что 1С отсылает данные в windows 1251, а принтер по умолчанию понимает UTF-8. Приходится предварительно сохранять данные в соответствующем формате в память пк, и только потом отсылать на принтер. Как изменить кодировку принтера я, к сожалению не разобрался. Читал у вас про смену кодировки принтера и про кодовые таблицы, но нигде их не нашёл. Не подскажете, где про это можно прочитать более подробно?

  • 12 CS  [Москва]

    Ага, ОТЛИЧНО! Ну тогда надо было мне тебе просто написать что там тупо СтрЗаменить и в файл.
    Не, компоненты не надо — у меня 7.7, которую я пишу с 2008 года. Переходить на 8 не стал, потому что база сугубо личная, инфы там навалом, и нужна она каждый день. А если взвесить — то из 8ки мне бы было надо только новый тип поля «Табличная Часть».

    Про кодировку. Попробуй его в GoLabel помучить. Там где-то в настройках принтера есть это дело. Я щас в другом месте, без принтера, поэтому точно не могу сказать что у меня стоит. Скорее всего Win-1251. Там проблема была в том, что некоторые моменты, именно ТОЛЬКО текст, надо было в UTF-8 слать. Причём клогда я написал свою утилитку эту, которая 8-битная и работает только с ANSI, проблема исчезла. Так что может быть там ещё сама компонента искажает что-то?

  • 13 Artem11

    Подскажите, а не пробовали напрямую в TCP порт 9100 этого притнтера писать?? Так ведь можно вообще без драйверов обойтись. Есть какие-то подводные камни этого метода??
    Попробовал подключиться к принтеру через телнет и пихнул комманды из буфера обмена — все распечаталось моментом.

  • 14 CS  [Москва]

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

  • 15 A_r_t_e_m

    Здравствуйте! Большое спасибо за статьи на тему печати! Но при запуске возникает ошибка из-за несовместимости в 64-х разрядной вин10. Есть скрин ошибки, но не могу его вставить(

  • 16 CS  [Москва]

    A_r_t_e_m Ну тут уж извиняй. Я скомпилировал под 32 бита, а под 64 мне нечем скомпилировать.

  • 17 A_r_t_e_m

    А можно тогда исходный код получить? Попробую скомпилировать под 64, если получится — вылюжу сюда, вдруг у кого-то такая же проблема.

  • 18 CS  [Москва]

    Заслал! Как соберёшь и протестишь — шли мне EXE, а я его в тот же архив включу!

  • 19 AlexMCFLY

    Спасибо за программу. Понадобилась возможность срочно очищать буфер задания на принтере Godex ZX1300i. Команда
    ~S,BUFCLR
    E
    помогла и отлично отрабатывает через RAWCMD на Windows 7 x64! На Windows 10 не пробовал.

  • 20 CS  [Москва]

    AlexMCFLY Спасибо! Мне приятно это слышать. Ураа!

Оставить отзыв

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