воскресенье, октября 22, 2017

Программируемый калькулятор: мой первый плагин WordPress



Задача: расставить на страницах сайта калькуляторы расчёта стоимости потолка, балкона, окна, кондиционирования.

Параметры должны быть настраиваемыми.

Сайт на WordPress. Соответственно, настройка в админке.

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

Загуглил, как устроен плагин WP (и сам WP заодно). На Хабре хорошие примеры. Немного поковырял, получил страницу в админке.

Сохранять и получать массив параметров удобно через вордпрессовские  get_option() / update_option().

Вставлять калькулятор в страницу - через shortcode с параметром.

Перешёл к самому интересному: скриптингу расчётов.

Всё, что участвует в расчёте, обозначим классом factor.

Калькуляторы не отнимают и не делят. Только два действия. Соответственно, нужны поля двух классов: add и mul. Первое прибавляет к итогу, второе - умножает итог.

При событиях change, keyup со всем, имеющим класс factor, вызывается пересчёт.

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

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

Покрутил так и эдак, пришёл к схеме: если среди $('.factor') есть что-то с классом freeze, запомнить итог на этот момент. Если дальше что-то имеет класс use-frozen, оперировать не итогом, а его замороженным значением.

Всё гениальное frozen.

Заодно добавим класс cm2m, значения этих полей при пересчёте переводим из сантиметров в метры на лету.

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

Если это хорошо упаковать (встроенный редактор калькуляторов, шаблоны внешнего вида, адаптив, i18n), годится как самостоятельный товар.

среда, декабря 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.

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

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