Итак, я написал уже два поста про 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
После этого мне понадобилось поразбираться с кодировками в консоли, чтобы все данные верно брались из параметров и передавались без искажений, и после этого у меня получился простенький готовый продукт. Вот как это всё выглядит:
Утилита RAWCommans для отправки RAW-команд на принтеры напрямую
Это обычная консольная утилитка. Все нужные библиотеки, кроме системных, интегрированы внутрь неё, так что она переносима и ничего лишнего не требует. У меня она работает на WinXP, и должна работать и на более старших виндах. Утилитка делает только одну вещь: открывает принтер по имени, создаёт задание печати с указанным именем и отправляет на принтер указанный файл. Поэтому утилита годится для ЛЮБОГО принтера маркировки с любым языком: никакие специальные команды принтеру она не шлёт, тупо отсылая содержимое файла. А что вы там в файле напишете — то на принтер и пойдёт.
Запускается она так: RAWCmd.exe «Название принтера» «Название задания» «Файл команд». Если параметры содержат пробелы — то их надо указать в кавычках. Например если принтер зовётся MyG500 — то указываем его напрямую. А если он зовётся Godex G500 — то надо указать в кавычках «Godex G500». У программки на каждый чих есть куча кодов возврата, так что все ошибки можно будет отследить в bat-файлах и других скриптах, если потребуется.
Я создал файлик из одной буквы «E» (конец команд для Godex) и запустил утилиту, чтобы показать как это отрабатывает:
Отправка тестового задания печати RAW на принтер
В итоге в очереди печати принтера создалось задание с нашим названием, которое содержит данные размером в три байта (один байт — буква «E» и два байта на конец и перевод строки).
Задание печати, созданное утилитой RAWCommands
Утилита лежит вот по этому адресу: CS-RAWCmd.rar (~60 кб). Пользуйтесь на здоровье, только сохраняйте мои копирайты.
Проекту исполнилось 15 лет! Поддержать проект материально, проспонсировать проекты Автора или сделать ему подарок можно на этой странице: "Донаты и Спонсорство, Список Желаний".
А давайте ему памятник поставим? Ну маленький хотябы.. :)
Кому — ему? Мне не надо =)
В похожей ситуации с usb-принтером, правда обычным, офисным, я поступил следующим образом — расшарил принтер, написал в командной строке net use \имя_компа\имя_расшаренного_принтера LPT1.
Прокатило.
Ну вот так все и предлагают по форумам делать. Но это колхоз, потому что лишняя шара появляется и так ещё по сетке не попечатаешь с разных компов.
Поэтому я вот и написал эту хреновину =)
Такой себе колхоз. Шара — тож не беда, чай не сетка ФСБ -). А на соседний комп с неменьшим успехом можно поставить дрова, расшарить принтер и т.д.
Ой, да даже и драйверы ставить не надо. Подключиться просто к принтеру с соседнеог компа, сказать net use, да и все.
Так мы разве спорим? Мне тот способ не нравится — это лишние настройки. Я хочу добиваться того, чтобы поставил принтер, дрова — и всё. Больше никаких херовин. А net use кажется при перезагрузке уже слетает.
Нет, не спорим, конечно. Что до net use, то он сохраняется по пользователю после перезагрузки. Если пользователей ходит несколько, то можно применить очевидный колхозный метод — прописать net use в автозагрузку.
Ага, прения устранены. Я его никогда не юзал и не знал, сохраняется ли подключени… а, хотя где-то ж была галка типа «Восстанавливать сетевые диски при входе в систему».
Здравствуйте, три ваши статьи в блоге здорово облегчили мне настройку Codex G330, благодарю вас! У меня есть ещё вопрос, мне нужно из 1С вызывать печать этикеток с заданными значениями, которые формируются из номенклатуры. Номенклатуры много и она в таблице. То есть мне нужно в 1С запустить цикл, где создавалась бы структура с кодом EZPL для принтера и сразу отправлялась бы на печать. Не могли бы вы привести кусок кода из вашей пробной программки, где реализуется отправка файла/структуры в принтер. Как загрузить изображение и создать код в GoLabel я уже разобрался. 1С Предприятие 8.3, Codex G330 по USB.
Привет! УРА! Вот кому-то сгодилось. А ты с 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.
Я с 1С к счастью на ты:) скачал с инфостата компоненту, которая послыает данные непосредственно в порт принтера, можно отсылать даже со строки с данными, я в эту строку и копирую все переменные от базы 1с. Большое спасибо за подробный ответ!Могу сбросить компоненты на почту, если нужно:) правда они под 1С 8.2.
Единственная проблема возникла с кодировкой текста, и тут я вспомнил вашу статью, то что 1С отсылает данные в windows 1251, а принтер по умолчанию понимает UTF-8. Приходится предварительно сохранять данные в соответствующем формате в память пк, и только потом отсылать на принтер. Как изменить кодировку принтера я, к сожалению не разобрался. Читал у вас про смену кодировки принтера и про кодовые таблицы, но нигде их не нашёл. Не подскажете, где про это можно прочитать более подробно?
Ага, ОТЛИЧНО! Ну тогда надо было мне тебе просто написать что там тупо СтрЗаменить и в файл.
Не, компоненты не надо — у меня 7.7, которую я пишу с 2008 года. Переходить на 8 не стал, потому что база сугубо личная, инфы там навалом, и нужна она каждый день. А если взвесить — то из 8ки мне бы было надо только новый тип поля «Табличная Часть».
Про кодировку. Попробуй его в GoLabel помучить. Там где-то в настройках принтера есть это дело. Я щас в другом месте, без принтера, поэтому точно не могу сказать что у меня стоит. Скорее всего Win-1251. Там проблема была в том, что некоторые моменты, именно ТОЛЬКО текст, надо было в UTF-8 слать. Причём клогда я написал свою утилитку эту, которая 8-битная и работает только с ANSI, проблема исчезла. Так что может быть там ещё сама компонента искажает что-то?
Подскажите, а не пробовали напрямую в TCP порт 9100 этого притнтера писать?? Так ведь можно вообще без драйверов обойтись. Есть какие-то подводные камни этого метода??
Попробовал подключиться к принтеру через телнет и пихнул комманды из буфера обмена — все распечаталось моментом.
Artem11 Нет, не пробовал: такое мне не пришло в голову, потому что я очень хотел сделать так, чтобы у меня поддерживалась виндовская очередь печати — чтобы задания на печать можно было в ней видеть и перезапускать.
Здравствуйте! Большое спасибо за статьи на тему печати! Но при запуске возникает ошибка из-за несовместимости в 64-х разрядной вин10. Есть скрин ошибки, но не могу его вставить(
A_r_t_e_m Ну тут уж извиняй. Я скомпилировал под 32 бита, а под 64 мне нечем скомпилировать.
А можно тогда исходный код получить? Попробую скомпилировать под 64, если получится — вылюжу сюда, вдруг у кого-то такая же проблема.
Заслал! Как соберёшь и протестишь — шли мне EXE, а я его в тот же архив включу!
Спасибо за программу. Понадобилась возможность срочно очищать буфер задания на принтере Godex ZX1300i. Команда
~S,BUFCLR
E
помогла и отлично отрабатывает через RAWCMD на Windows 7 x64! На Windows 10 не пробовал.
AlexMCFLY Спасибо! Мне приятно это слышать. Ураа!