среда, декабря 28, 2016

Победа над SIP близка как никогда


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

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

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

Опробовал у себя (Debian); работает. Написал крошечный скрипт на событие "call is answered":

#!/bin/bash

xdg-open http://host/callerid.php?$(echo ${SIP_FROM}  | cut -d '"' -f2 | cut -d '+' -f2) &
echo end

по частям:

xdg-open: выполнить дефолтное действие; для URL это открытие предпочитаемого браузера.

callerid.php: скрипт, ожидающий чего-то похожего на телефонный номер, приводящий его к нормальному виду (10 цифр) и выполняющий поиск в базе клиентов.

echo ${SIP_FROM}: сюда выводится переменная SIP_FROM, устанавливаемая в окружении скрипта Twinkle'ом. В начале её содержимого идёт номер в кавычках.
Первый cut разбивает строку по кавычкам, второе поле этого результата - телефонный номер с плюсом в начале; от плюса избавляет второй cut, результат идёт в http-запрос.

echo end: передать Twinkle, что скрипт закончил работу.

В момент ответа на звонок у оператора выскакивает браузер со страницей, где он видит телефон и имя клиента (если он повторный).

Дальше, как водится, был геморрой: в Убунту 14 Twinkle без GUI; после апгрейда до 16 выяснилось, что Twinkle тупо сегфолтит на звонке, что характерно, именно на Убунте (ох уж ейная самобытность, сколько крови выпила). Пришлось (благо на ноутах, кроме браузера и SIP-клиента, ничего нет) быстренько перескочить на Debian Stretch, после чего уже всё и заработало.


четверг, декабря 15, 2016

Linux -> Windows -> HP LaserJet

Потребовалось настроить печать из CUPS на HP LJ, зашаренный на Windows-машине. Пишу в том числе и для памяти: сколько раз приходилось повторно решать проблему, не сделав памятки.

1) На Linux должна быть установлена samba и HPLIP.

2) На Windows нужно зашарить принтер с удобным (коротким буквоцифровым) именем.

3) Проверяем, что Windows-хост виден Линуксу:

smbtree

Ниже информации о самом себе должен быть хост Windows, например:

\\DESKTOP-Q8VFISC

3) Проверяем, что принтер виден Линуксу. uname - имя пользователя на Windows-хосте:

smbclient -L \\DESKTOP-Q8VFISC -U uname

Sharename       Type      Comment
---------       ----      -------
ADMIN$          Disk      Удаленный Admin
C$              Disk      Стандартный общий ресурс
IPC$            IPC       Удаленный IPC
P2035           Printer   HP LaserJet P2035
print$          Disk      Драйверы принтеров

4) Открываем администрирование принтеров в CUPS, добавляем принтер, выбираем из сетевых принтеров опцию Windows Printer via SAMBA. Строка подключения:

smb://uname:pwd@host/printername

Если принтер зашарен без пароля, uname:pwd@ опустить. При повторных изменениях принтера имя-пароль будут сбрасываться!

5) Выбираем драйвер. Здесь 2 момента:

- Драйверы некоторых принтеров HPLIP требуют закрытого плагина (в имени драйвера указано requires proprietary plugin. Плагин устанавливается в консоли:

hp-plugin -i

- Всё готово, но в CUPS задание печати помечено Filter failed. В /var/log/cups/error.log написано:

Error: This module is designed to work with HP Printers only

У меня это решилось сменой драйвера с ZJS на PCL, т. е. даёт ошибку

HP LaserJet p2035 zjs, hpcups 3.16.11, requires proprietary plugin

но работает

HP LaserJet p2035 pcl3, hpcups 3.16.11, requires proprietary plugin

вторник, ноября 15, 2016

НЬЮЗЕНБЕРГИАДА, или Неожиданное путешествие по воздуху, суше и морю на ту сторону Курильской гряды и обратно


- У меня люди едут на Итуруп.

- Замечательное место, - заметил я. - Только летом там лучше.

Кончался октябрь.

- Там пароход сломался. Едет специалист, везёт запчасть.

- Ты сказал: люди.

- Ещё ты.

- ??!

- Помоги, он сам не доберётся. Ты местный.

- Я с Кунашира. Итуруп только с рейда видел.

- Какая, б***ь, разница? Все Курилы одинаковые.

- Запчасть тяжёлая?

- Килограмм десять-пятнадцать.

* * *

Это был пролог, а сейчас второе ноября, среда, мы с Родионом пьём лимонад из картонных стаканчиков с символикой авиакомпании «Аврора» на борту Bombardier Q-400 авиакомпании «Аврора», рейс 801 Южно-Сахалинск — Курильск. Стаканчики принесла девушка в форме авиакомпании «Аврора», та же самая девушка, что вчера приносила сэндвичи и апельсиновый сок на борту Bombardier Q-400 авиакомпании «Аврора», рейс из Владивостока в Южно-Сахалинск. Ещё за час до того, первого «Бомбардье», мы встретили в аэропорту рейс из Кореи, на котором некто Сергей привёз ту самую запчасть и вручил нам, как 11-килограммовую эстафетную палочку.

Родион — судомеханик. Запчасть — увесистая. Я — сопровождающий.


суббота, сентября 03, 2016

Лихтенштейновская женщина в Inkscape: паттерны - это просто

Нужно было картинку с девушкой в манере Роя Лихтенштейна. Сделать в Inkscape. Высотой 3 метра.

Сама девушка была подготовлена в Gimp и трассирована.




Теперь надо было сделать растр с точкой размером горошины. Очевидно, надо использовать паттерн. Это одна из возможностей Inkscape, которых я никогда или почти никогда не касался. А зря. Всё оказалось очень непринуждённо.

1) Надо сделать примерно 50% растр с круглой точкой. Рисуем квадрат, в нём  круг размером в 2 раза меньше, примерно так:


2) Круг делаем красным, квадрат - прозрачным. Выделяем и делаем Objects -> Pattern -> Objects to pattern.

3) Рисуем контур под заливку и заливаем своим паттерном. Берём инструмент Node. Контур должен быть активен. Пока он активен, операции над контролирующими точками оригинала будут влиять на заливку контура. Так очень легко получить нужную частоту и угол растра. 



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

Поскольку Inkscape рисует SVG, то заливка паттерном, думаю, внутренне реализована как background-image, параметры которого регулируются инструментом Node.

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









понедельник, августа 08, 2016

Ушёл на базу. Через шифрованную дырку.

С открытием второй пиццерии (с моей точки зрения - с развёртыванием второй системы управления производством) возникли вопросы взаимодействия:

Как дать возможность оператору отправить заказ на нужную пиццерию?

Как формировать консолидированные отчёты по двум точкам?

Как синхронизировать меню, цены, акции, дисконты, купоны - всё, что может измениться в любой момент?

Вариант master/slave в MySQL сразу не рассматривал, потому что схема должна быть такой, чтобы пережить атомную бомбардировку: уцелевшие пиццерии должны продолжить автономную работу несмотря на оборванные провода и исчезнувшие соединения.

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

Либо обращаться к базе удалённо.

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

Немножко почитал интернеты и понял, что понадобится:

autossh - надстройка над ssh, поддерживающая соединение в рабочем состоянии, пока это возможно;


И всё.

К слову, Гугл по поиску autossh в третьей позиции даёт ветку под названием "Чтоб вы знали: autossh - полное дерьмо". Я её внимательно изучил. Она меня не убедила.

Ещё нужен скрипт для управления autossh как службой, он находится здесь и отлично встраивается в Debian.

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

Дальше - с отвёрткой в зубах - в PHP-класс работы с SQL. Здесь делаем выбор параметров соединения из массива предопределённых вариантов. Важно: если сервером указывать localhost, запрос такого-то порта будет проигнорирован. Поэтому указываем 127.0.0.1. Вроде хрен и редька, а вот хрен.

Настало время первой проверки. Пишем коротенький скрипт, который запрашивает максимальные идентификаторы из таблиц заказов серверов, разнесённых на некоторое количество километров:

Холишыт, это действительно работает!

Дальше так: в интерфейс операторов, принимающих заказы с доставкой, добавляем выбор пиццерии:


Чуть позже по умолчанию будет та, чей этот клиент географически.

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

Дальше надо поэкспериментировать и понять, как лучше строить консолидированные отчёты: federated table?

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

четверг, августа 04, 2016

Печать чеков на Posiflex Aura: траблшутинг

Как уже сообщалось, произошло обарывание принтера Posiflex Aura для печати из-под CUPS; как оказалось, не окончательное.

При попытке перехода на новую схему оказалось, что некоторые чеки выходят в виде вертикальных полос либо просто чёрным листом с обрывком контента внизу. Их PDF выглядят такими же, как и нормально печатающиеся. Загадка? Загадка.

Надо думать. Теорию суперструн вон придумали, а ср@ные полоски на бумаге не осмыслим? Так не бывает.

Вот драйвер принтера переварил PDF и выдал растровую страницу. Вот она пошла в принтер. Может переполниться память? Если она есть, то может.

Посмотрел спецификацию принтера. Памяти страниц нет. Есть входной буфер размером 10К.

Значит, принтер не запоминает всё задание перед печатью. Выводит поток на ленту и тут же забывает. Логично: все нелазерные принтеры так работают.

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

Теперь смотрим на PDF, который мы печатаем. Начало.


Что, если жирный логотип сразу забивает буфер и не успевает утечь в дренаж? Buffer overrun. Скорее всего, часть данных будет потеряна; затем напечатается обрывок нижней части. Размер чека будет таким, будто всё напечатано правильно: скорее всего, он передаётся в задании.

Это именно то, что мы имеем.

Проверяем. Открываем шаблон PDF и комментим лого. Надо же - всё печатается хорошо.

Исправляем в стиле логотипа ширину: 40 мм вместо 70. Полёт нормальный.

Ставим 50 мм. Нормально.

Где-то между 50 и 70 находится критический размер, дающий битмап под 10К. Я не стал его выяснять: 5-сантиметровое лого ОК.

Если всё верно, теперь при любом размере чека печать будет происходить правильно. Для проверки лезем в базу данных:

select count(ID) as c, order_id 
from ########## 
group by order_id order by c desc limit 1

Получаем заказ из 52 позиций, это исторический максимум со среза базы в январе.

Печатаем его чек. Две 32-сантиметровые страницы, всё чётко. С логотипом.

Последнее: уменьшенный логотип имеет некрасивые края: dithering. Открываем Gimp, меняем печатный размер лого: 50 мм при разрешении 8 dpmm (из спецификации). Переводим в 1-битный битмап и сохраняем. О, печатается намного лучше!

Последнее. Открываем скрипт печати и переключаем вывод сокета TCP на очередь CUPS.

Трибуны беснуются.

пятница, июля 29, 2016

Траблшутинг: как модем перестал, а потом снова начал слать СМС


Жил-был в одной пиццерии модем, выполнявший единственную функцию: при оформлении онлайн-заказа слал на номер клиента число из диапазона 1000-9999. Слал-слал, да и перестал.

В error-логе Apache обнаружилось, что gsmsendsms заканчивается неудачей с ошибкой: неожиданный ответ: ^RSSI: 9.

RSSI - показатель силы сигнала. Чо к чему?

Полез к модему через minicom. Увидел, что модем регулярно шлёт RSSI: <цифра> и тип соединения. Очевидно, это и есть "неожиданный ответ". gsmsendsms ожидала на свою сентенцию что-то типа ОК, а получила, натурально, уровень сигнала зачем-то.

Выключил эти сообщения (есть такая АТ команда). Теперь gsmsendsms заканчивалась с ошибкой 500.

500 - это ошибка типа "ХЗ". Что-то не так, не технически, а вообще. Непорядок во внешней вселенной.

Вынул SIM-карту из модема, поставил в телефон. Она оказалась заполненной. Хранилище на 15 сообщений было полно предупреждениями МЧС о грядущих ненастьях.

Удалил сообщения и поставил карту назад в модем. Она стала отправлять сообщения.

Проблема была решена, но в ближайшей перспективе. МЧС ведь не остановится. Ненастья грядут.

Изучение AT команд открыло, что AT+CMGD=X удалит из хранилища сообщение номер X. А AT+CMGD=X,4 удалит всё, игнорируя значение X.

Также оказалось, что AT команды можно тупо слать в /dev/ttyUSBx, например, через echo -n.

Осталось вставить это в crontab рута.

Оставил на завтра: слишком много свершений за один день.

Поиск по этому блогу