пятница, мая 22, 2009

Motorola Symbol MC3090, Zebra RW 420 и русский шрифт

Столкнулся тут с проблемой - мобильная печать этикеток со штрих-кодом...
Дано:
1) Терминал Motorola (Symbol) MC3090 Windows CE 5.0
2) Принтер Zebra RW430
3) 1C:Предприятие 8.1 (УПП)
4) WiFi - точка доступа.


Решено не использовать всякие Mobile Logistic и прочие продукты сторонних производителей - да и дорабатывать их надо под задачи предприятия....

MC3090 - русский язык отображает, но русской консоли нет. Поиск по нету ничего вразумительного не дал - ни один руссификатор от Win Mobile не подходит.... Фиг с ним - решено: На терминале ввод ограничим максимум числами.
Далее стал думать - как собственно работать с 1С. На терминале есть клиент Remote Desctop - быстро навалял мини-формы для 1С, подключил принтер к компу - вуаля- печатать ярлыки можно...

А вот с остальным - затык! Штрих коды через терминал не почитаешь - лазер не работает пока ему пинка программно не дашь... DataWedge не выход...

Решено забить на Remote Desctop и взять в руки Compact Farmework в купе с Symbol EMDK for .Net

Реализовалось всё довольно быстро: в 1С реализован Web-сервис, в терминале - клиент. Плюсов дофига - никаких выгрузок-загрузок, не нужно SQL server CE и прочие Lite, нажал на терминале кнопочку- списочек документов получил, выбрал, пошел печатать ярлыки...

Ляпота....

Принтер RW420 в таком случае будет иметь свой IP и данные будут приходить на определенный порт в виде команд CPCL.
Тоже ляпота....

Стоп! Ляпота кончилась - в Zebra RW 420 нет шрифтов с кириллицей!
хотя по русски надо вывести всего две строчки.... Иэх...
Полазив в нете - ничего не нашел. Label Vista позволяет создать шрифт для принтера из шрифта True Type, НО! Только для первых 127 символов. Победить это не удалось. ZebraDesigner Font Downloader вообще работать отказался :(

После недолгих раздумий нашелся выход - печать в графике (выводить название товара в Bitmap и затем печатать на принтер). Плюсы - использование любых шрифтов.
Затык в том, что Zebra RW420 принимает только формат PCX. Конвертация в этот формат из изображений, поддерживаемых Compact Framework - отдельная песня...

Есть еще один вариант - использовать команду EG или CG. (Смотрите мануал по CPCL).
Методом тыка выбрана CG. (Кстати Label Vista всю этикетку загоняет в графику и печает с помощью этой самой команды).
Теперь надо думать как ей подсунуть картинку.
Вообщем, методом проб и ошибок задача была решена.
И вот решением её я и хочу с Вами поделиться. (это не конечный вариант, поскольку все константы затем были вынесены в настройки, а скорее наиболее понятная форма).

public static class BarCodePrinter
{
/// Печать этикетки
public static void PrintGoodsLabel(BarCode code, int count)
{
try
{
//Рисуем битмап с двумя строками (наименование товара и наименование серии)
var bmp = CreateBitmap(code.GoodsName, code.SerieName);

//Создаем заголовок формы (см. CPCL-programming manual)
var top = new StringBuilder();

top.AppendLine("! 0 200 200 400 " + count);
top.AppendLine("LABEL");
top.AppendLine("CONTRAST 0");
top.AppendLine("TONE 0");
top.AppendLine("SPEED 5");
top.AppendLine("PAGE-WIDTH 800");
top.AppendLine("GAP-SENSE 50");
top.AppendLine(";// PAGE 0000000008000400");

//формируется сама команда CG
top.Append("CG ");

top.Append(bmp.Width / 8 + " "); //Ширина рисунка в байтах
top.Append(bmp.Height + " "); //Высота рисунка в пикселях

top.Append(5 + " "); //x - координата рисунка на этикетке
top.Append(5 + " "); //y - координата рисунка на этикетке

//Конвертируем битмап в набор байтов для команды CG
var imgBuffer = BitmapToByteBuffer(bmp);

//Это будет нижняя часть этикетки
var bottom = new StringBuilder(Environment.NewLine);

//Вывод штрих-кода
bottom.AppendLine("BT 7 3 5");
bottom.AppendLine("B EAN13 5 1 200 120 115 " + code.Code);
bottom.AppendLine("BT OFF");

bottom.AppendLine("FORM");
bottom.AppendLine("PRINT");

//Отправляем на печать
SendToPrinter(top.ToString(), imgBuffer, bottom.ToString());
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Ошибка!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation,
MessageBoxDefaultButton.Button1);
}
}

///Конвертация битмапа в байт-массив
private static byte[] BitmapToByteBuffer(Bitmap bmp)
{
var imgBuffer = new byte[bmp.Width/8*bmp.Height];
var i = 0;
for (var ycount = 0; ycount < bmp.Height; ycount++)
{
for (var xcount = 0; xcount < bmp.Width; xcount += 8) //формируем по 8 битов
{
//формируем байт (типа... можно, конечно и элегантней)
var b = bmp.GetPixel(xcount, ycount) == Color.Black ? 1 : 0;
for (var j = 1; j < 8; j++)
{
b <<= 1;
b += bmp.GetPixel(xcount + j, ycount) == Color.Black ? 1 : 0;
}

//байт за байтом добавляем к результату
imgBuffer[i] = (byte) b;
i++;
}
}
return imgBuffer;
}

///Выводим две строчки в битмап
private static Bitmap CreateBitmap(string first, string second)
{
var img = new Bitmap(784, 112, PixelFormat.Format16bppRgb555);
// PixelFormat.Format1bpp был бы, наверное, лучше (меньше по размеру), но он не поддерживается на Windows CE :(

var graphicImage = Graphics.FromImage(img);
graphicImage.Clear(Color.White);

var drawFont = new Font("Arial", 24, FontStyle.Bold);
var drawBrush = new SolidBrush(Color.Black);

const float x = 10.0F;
const float y = 10.0F;

graphicImage.DrawString(first, drawFont, drawBrush, x, y);
graphicImage.DrawString(second, drawFont, drawBrush, x, y + 30);

return img;
}

///Берём IP endpoint принтера
private static IPEndPoint GetPrinterEndPoint()
{
var address = IPAddress.Parse("192.168.0.221"); //адрес

if (address == null)
throw new NullReferenceException("Не указан адрес принтера!");

return new IPEndPoint(address, 6101); //и порт...
}

/// Печатаем...
private static void SendToPrinter(string top,byte[] imgBuffer, string bottom)
{
var printerEndPoint = GetPrinterEndPoint();

var clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

clientSocket.Connect(printerEndPoint);
clientSocket.Send(Encoding.ASCII.GetBytes(top)); //отправляем шапку
clientSocket.Send(imgBuffer); //отправляем картинку
clientSocket.Send(Encoding.ASCII.GetBytes(bottom)); //отправляем подвал (штрих-код и команда PRINT!)

clientSocket.Close(); // усё!!!
}
}


Ляпота...

понедельник, мая 04, 2009

По мотивам: Ускоряем сборку проектов в Visual Studio

Блог Александра Бындю: Ускоряем сборку проектов в Visual Studio

Александр в своём блоге рассказал о том, как можно повысить производительность в VS.


Я решил попробовать...
Для начала я нашел бесплатный (для русскоязычной версии) RAMDisk "Enterprise" для Windows 2000 / Windows XP / Server 2003 / Vista на страничке

Linkd я искать не стал, поскольку в Win2008 (как и в Висте) есть команда mklink, впрочем это мне тоже не понадобилось...

Поскольку у меня не было Nant и скачивать я его не захотел - решил я написать скрипт в PowerShell и вот что у меня получилось:


gci d:\NewProject\ -Recurse -Include bin,obj -name | New-Item -Path V:\NewProject\ -name {$_} -ItemType directory #создаем каталоги на виртуальном диске
gci d:\NewProject\ -Recurse -Include bin,obj | rmdir -Recurse -Force #удаляем каталоги
gci v:\NewProject\ -Recurse -Include bin,obj -Name | New-Symlink -Path {"d:\NewProject\"+$_} -Target {"v:\NewProject\"+$_} -Verbose #создаём линки


Как видите - всё просто.
Да, у меня уже было установлено PowerShell Community Extensions в котором имеется команда New-Symlink.