Вывод информации на дисплей покупателя

в 8:15, , рубрики: bash, linux, дисплей покупателя, Программирование

Появился дисплей покупателя. Интересно стало попробовать его использовать в качестве информационного табло для вывода информации о текущем дне, времени до конца рабочего дня/недели, информации о погоде, курсе валют.

При этом не хотел использовать ресурсоёмкие приложения и свой ПК. Остановился на связке мини-ПК Raspberry + Linux + Дисплей покупателя.

@Дисплей покупателя с информацией | center | 700x0

@Raspberry Pi 2 | center | 300x0

Необходимые замечания

Установку и настройку ОС Linux на устройство Raspberry в данном материале не рассматриваю.

Для редактирования текста в среде Linux использовал редакторы nano и mcedit.
Для доступа к мини-ПК на базе ОС Linux из среды Windows пользовался клиентами для удаленного доступа по протоколу SSH — KiTTY/PuTTY.
Для передачи файлов между ОС Windows и Linux использовал программу WinSCP.

Bash — командный интерпретатор (командная оболочка).
Bash — аббревиатура от "Bourne-Again Shell" ("возрождённая" оболочка). Ключевые слова, синтаксис и другие основные особенности языка были заимствованы из другого командного интерпретатора sh (сокращение от shell).
Bash — это ещё и мощный язык программирования.

Я занимаюсь сопровождением программных продуктов на базе 1С и для меня это было возможностью самому познакомится с программированием в среде Linux.
В меру своего понимания буду разъяснять выполняемые команды. Это сделано с целью большого охвата аудитории.

Что использовал?

  • Одноплатный компьютер Raspberry Pi 2 Model B v1.1 с установленной ОС Raspbian GNU/Linux 9.4 (stretch).
  • Дисплей покупателя POSUA LPOS-VFD USB.
  • Командный интерпретатор bash.

1 этап. Подключение и настройка дисплея покупателя

После того как присоединили к USB-порту дисплей покупателя (ДП) выясним параметры подключенного устройства. В терминале выполним команду:

usb-devices

Получим список присоединенных USB устройств к Raspberry:

T:  Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=480 MxCh= 1
D:  Ver= 2.00 Cls=09(hub  ) Sub=00 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=1d6b ProdID=0002 Rev=04.14
S:  Manufacturer=Linux 4.14.69-v7+ dwc_otg_hcd
S:  Product=DWC OTG Controller
S:  SerialNumber=3f980000.usb
C:  #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=0mA
I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub

T:  Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=480 MxCh= 5
D:  Ver= 2.00 Cls=09(hub  ) Sub=00 Prot=02 MxPS=64 #Cfgs=  1
P:  Vendor=0424 ProdID=9514 Rev=02.00
C:  #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=2mA
I:  If#= 0 Alt= 1 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=02 Driver=hub

T:  Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=480 MxCh= 0
D:  Ver= 2.00 Cls=ff(vend.) Sub=00 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=0424 ProdID=ec00 Rev=02.00
C:  #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=2mA
I:  If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=ff Driver=smsc95xx

T:  Bus=01 Lev=02 Prnt=02 Port=01 Cnt=02 Dev#=  4 Spd=12  MxCh= 0
D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=0000 ProdID=0131 Rev=01.00
S:  Manufacturer=www.posua.com
S:  Product=POSua LPOS-II-VFD USB CDC
C:  #Ifs= 2 Cfg#= 1 Atr=a0 MxPwr=16mA
I:  If#= 0 Alt= 0 #EPs= 3 Cls=02(commc) Sub=02 Prot=01 Driver=usbserial_generic
I:  If#= 1 Alt= 0 #EPs= 2 Cls=03(HID  ) Sub=00 Prot=00 Driver=usbhid

Из информации полученной командой находим строку Product=POSua LPOS-II-VFD USB CDC. Это наш дисплей покупателя. В этой секции нам нужна строка Vendor=0000 ProdID=0131 Rev=01.00. А конкретно vendor=0000 prodID=0131. Так идентифицирует себя устройство.

Для корректной работы с ДП необходимо загрузить модуль работы с USB в ядро системы. Выполняем команду с повышенными правами:

sudo modprobe usbserial vendor=0x0000 product=0x0131

modprobe — программа для добавления модулей в ядро Linux. usbserial — модуль ядра который обеспечивает эмуляцию COM-порта на USB устройствах. 0x – означает шестнадцатеричный формат.

Так как у меня подключено одно USB-устройство, то в системе Linux автоматически получает файл ttyUSB0. Такова важная особенность взаимодействия с устройствами в ОС Linux – работа с устройством как с файлом. Файлы устройств хранятся в каталоге /dev.

Для корректной работы с ДП установим скорость передачи данных:

stty -F /dev/ttyUSB0 9600

stty — команда устанавливает параметры терминального ввода/вывода для устройства. -F — устройство. В нашем случае дисплей покупателя /dev/ttyUSB0. И для данного устройства устанавливается скорость 9600 бод.

Теперь можно попробовать вывести приветственное сообщение (пока на английском):

echo "Hello!" > /dev/ttyUSB0

Если всё сделали правильно, то на экране появится наше сообщение. Подробнее о команде ниже.

2 этап. Программирование

На предыдущем этапе мы вывели сообщение на английском языке поверх заставочного сообщения устройства. И это не очень красиво.

Для очистки экрана дисплея выполним команду:

echo -e -n "x0cx0b" > /dev/ttyUSB0

echo — команда вывода в терминал. Опция -e — включает поддержку вывода escape-последовательностей, -n — указывает, что не надо выводить перевод строки. Допускается запись -en.

Сочетания символов, состоящих из обратной косой черты , за которой следует буква или набор цифр, называются escape-последовательностями.

— очистка экрана дисплея и отмена строчного режима, 0b — перемещает курсор в верхнюю крайнюю левую позицию. Символ > — управление потоком (перенаправляет вывод). В данном случае в файл /dev/ttyUSB0 нашего устройства. Если просто выполнить команду echo "Hello!", то в окне терминала появится текст, указанный в кавычках.

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

stty 9600 < /dev/ttyUSB0

Ну и для вывода сообщений на русском языке выполним:

echo -n "Привет!" | iconv -f UTF-8 -t CP866 > /dev/ttyUSB0

| — перенаправляет вывод одной команды на вход другой (конвейер). В нашем случае последовательность символов "Привет!" не выводится в файл устройства сразу, а передаётся на "конвертирование" утилите iconv. iconv — преобразует из одной кодировки в другую.
Командный интерпретатор bash позволяет не только выполнять команды непосредственно в терминале, но и писать файлы-скрипты.

Скрипт — обычный текстовый файл с последовательностью выполняемых команд.

Для того, чтобы bash понимал, что это "его" в начале файла указывается #!/bin/bash. А для непосредственного выполнения скрипта надо выполнить команду:

sudo chmod u+x namefile.sh

Где namefile.sh — файл скрипта. Расширение sh — означает, что это файл-скрипт bash. chmod – программа для изменения прав доступа к файлам и каталогам. u+x — устанавливает право на выполнение файла для текущего пользователя.

Решение задачи выполним двумя скриптами. Первый скрипт — основной (dispos.sh). Он выводит всю необходимую информацию на дисплей покупателя. Второй — вспомогательный (parse.sh) получает данные погоды, сервисов котировок валют и записывает данные в промежуточные файлы. Промежуточные файлы с данными используются в первом скрипте.

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

sudo chmod +x dispos.sh
sudo chmod +x parse.sh

Обратите внимание, что используется просто +x. Это "укороченная" версия u+x.

Скрипты нужно запускать с определённой периодичностью. Для этого воспользуемся стандартным планировщиком crontab. Для редактирования служит команда:

crontab -e

В планировщик добавим две строки:

*/20 * * * * /home/pi/parse.sh
*/1 * * * * /home/pi/dispos.sh

Скрипт parse.sh выполняется каждые 20 минут, а скрипт dispos.sh каждую минуту.

Перед первоначальным выводом на дисплей покупателя прежде надо выполнить скрипт parse.sh который получит первичные данные о погоде и валюте.

./parse.sh

Далее я приведу полные тексты скриптов с короткими комментариями.

Описание файлов-скриптов

Файл dispos.sh

файл dispos.sh

#!/bin/bash
# Данный скрипт выводит информацию на дисплей покупателя POSua LPOS-VFD.
# По-умолчанию порт ttyUSB0.
# Для определения устройства как tty используется команда:
# modprobe usbserial vendor=0x0000 product=0x0131.
# Где 0x0000 и 0x0131 адрес устройства, определяемый командами
# usb-devices, lsusb или dmesg.
# Установка скорости передачи stty 9600 < /dev/ttyUSB0.
# Получение данных о погоде и валюте реализовано отдельным скриптом parse.sh
# Оба скрипта прописаны в crontab и выполняются с разными временными интервалами.
# ****************************************************************

# Глобальные переменные
# ttyUSB - устройство на которое выводим информацию (POS-дисплей)
DEV_DISPLAY="/dev/ttyUSB0"

# Определение дня недели и конца рабочего дня
# Все рабочие дни, кроме пятницы, заканчиваются в 18:00:00
# В пятницу в 17:00:00
TIME_OF_WORKDAY="18:00:00"

if (( $(date "+%u") >= 5 )); then
    TIME_OF_WORKDAY="17:00:00"
fi

# Определяем сколько до конца недели (пятница 17:00:00)
# с текущей даты в секундах
DAY_OF_WEEKEND=`date +"%s" --date="friday 17:00:00"`

# ****************************************************************
# Функции для работы с дисплеем покупателя

# Функция очистки дисплея
disp_clear(){
    echo -en "x0cx0b" > "${DEV_DISPLAY}"
}

# Функция перевода курсора на следующую строку
disp_cr(){
    echo -e "x0b" > "${DEV_DISPLAY}"
}

# Функция непосредственного вывода строки на дисплей
disp_print(){
    echo -n $1 | iconv -f UTF-8 -t CP866 > "${DEV_DISPLAY}"
}

# ****************************************************************
# Основная часть скрипта

# 1. Вывод текущей даты
disp_clear # Очистка дисплея
disp_print "Сегодня: `date "+%A"`"
disp_cr # Перевод на следующую строку
disp_print "  `date "+%d.%m.%Y %H:%M"`"

sleep 8

# ****************************************************************
# 2. Вывод информации до конца рабочего дня
disp_clear
disp_print "  До конца раб. дня:"
disp_cr

HOURS=$(( ( $(date +%s --date=$TIME_OF_WORKDAY) - $(date +%s) ) / 3600 ))
MINUTES=$(( (( $(date +%s --date=$TIME_OF_WORKDAY) - $(date +%s) ) - $HOURS * 3600) / 60 ))

# Определяем закончился рабочий день или нет
if (( $MINUTES > -1 )); then
    OUTPUT_TIME="   ${HOURS} час. ${MINUTES} мин."
else
    OUTPUT_TIME="    ЗАКОНЧИЛСЯ!"
fi

# Вывод время до конца рабочего дня
disp_print "${OUTPUT_TIME}"

sleep 8

# ****************************************************************
# 3. Вывод информации до конца рабочей недели
disp_clear
disp_print "До конца раб. недели:"
disp_cr

DAYS=$(( ($DAY_OF_WEEKEND-$(date +%s)) / (24*3600) ))
HOURS=$(( (($DAY_OF_WEEKEND-$(date +%s)) - ($DAYS*24*3600)) / 3600 ))
MINUTES=$(( (($DAY_OF_WEEKEND-$(date +%s)) - ($DAYS*24*3600) - ($HOURS*60*60)) / 60 ))

# Определяем закончилась рабочая неделя или нет
if (( $MINUTES > -1 )); then
    OUTPUT_TIME="${DAYS} дн. ${HOURS} час. ${MINUTES} мин"
else
    OUTPUT_TIME="    ЗАКОНЧИЛАСЬ!"
fi

# Вывод времени до конца рабочей недели
disp_print "${OUTPUT_TIME}"

sleep 8

# ****************************************************************
# 4. Вывод информации о погоде

# 4.1. Считываем данные из временного файла

LINE1=$(sed -n '1{p;q}' /tmp/weather.txt)
DISPLAY_LINE1=${LINE1:0:19}
DISPLAY_LINE2=${LINE1:19:19}

# Вывод погоды на дисплей (2 строками)
disp_clear
disp_print "${DISPLAY_LINE1}"
disp_cr
disp_print "${DISPLAY_LINE2}"

sleep 4

# 4.2. Вторая часть погоды
LINE1=$(sed -n '2{p;q}' /tmp/weather.txt)
DISPLAY_LINE1=${LINE1:0:14}
DISPLAY_LINE2=${LINE1:14:19}

# Вывод погоды на дисплей (2 строками)
disp_clear
disp_print "Ожид ${DISPLAY_LINE1}"
disp_cr
disp_print "${DISPLAY_LINE2}"

sleep 8

# ****************************************************************
# 5. Вывод информации о банковской валюте

# Считываем данные из временного файла

# Доллар
DOLLAR=$(sed -n '1{p;q}' /tmp/ex.txt)
DOLLAR=${DOLLAR//–/-}

# Евро
EURO=$(sed -n '2{p;q}' /tmp/ex.txt)
EURO=${EURO//–/-}

# Вывод валюты на дисплей
disp_clear
disp_print "Доллар: ${DOLLAR}"
disp_cr
disp_print "Евро: ${EURO}"

sleep 8

# ****************************************************************
# 6. Вывод информации о электронной валюте

# Считываем данные из временных файлов

# BTC
while read line
do
    BTC=${line:0:13}
done </tmp/bitcoin.txt

# ETH
while read line
do
    ETH=${line:0:13}
done </tmp/ethereum.txt

# Вывод валюты на дисплей
# Заменяем разделитель точка на запятую
disp_clear
disp_print "BTC: ${BTC//./,}"
disp_cr
disp_print "ETH: ${ETH//./,}"

#sleep 8

# ****************************************************************
# 7. Вывод произвольной информации на дисплей

# Здесь необходимо написать любую строку (макс. длина 20 символов)

#DISPLAY_LINE1="С Новым Годом!"
#DISPLAY_LINE2="С Днём России!"

# Вывод произвольной информации на экран
#disp_clear
#disp_print "${DISPLAY_LINE1:0:19}"
#disp_cr
#disp_print "${DISPLAY_LINE2:0:19}"

Комментарии

Для вывода текущей даты служит команда date. Пример,

echo `date "+%d.%m.%Y %H:%M"`

После выполнения получаем дату вида: 20.05.2019 12:11.

Для расчёта времени до конца дня воспользуемся дополнительной переменной TIME_OF_WORKDAY и установим значение TIME_OF_WORKDAY="18:00:00". Ну а далее рассчитаем часы и минуты до конца рабочего дня:

HOURS=$(( ( $(date +%s --date=$TIME_OF_WORKDAY) - $(date +%s) ) / 3600 ))
MINUTES=$(( (( $(date +%s --date=$TIME_OF_WORKDAY) - $(date +%s) ) - $HOURS * 3600) / 60 ))

Символ $ — указывает на то, что это переменная.
Символ # — комментарий.

date +%s — получаем текущую дату и время в секундах.
date +%s --date=$TIME_OF_WORKDAY — получаем время в секундах до TIME_OF_WORKDAY ("18:00:00").

Расчет времени до конца рабочей недели:

DAYS=$(( ($DAY_OF_WEEKEND-$(date +%s)) / (24*3600) ))
HOURS=$(( (($DAY_OF_WEEKEND-$(date +%s)) - ($DAYS*24*3600)) / 3600 ))
MINUTES=$(( (($DAY_OF_WEEKEND-$(date +%s)) - ($DAYS*24*3600) - ($HOURS*60*60)) / 60 ))

Где DAY_OF_WEEKEND=`date +"%s" --date="friday 17:00:00"` — время в секундах с текущего момента времени до пятницы 17:00:00.

Часть скрипта реализована с помощью функций. Например,

# Функция очистки дисплея
disp_clear(){
    echo -en "x0cx0b" > "${DEV_DISPLAY}"
}

disp_clear() — название функции. В {} указываются выполняемые команды.

Переменная DEV_DISPLAY является "глобальной" и задаётся вначале скрипта и соответственно DEV_DISPLAY="/dev/ttyUSB0".

Чтение данных из файла, например конкретной строки (1):

LINE1=$(sed -n '1{p;q}' /tmp/weather.txt)

sed — это текстовый редактор, выполняющий операции редактирования над информацией в стандартном потоке ввода или файле. Параметр -n – выводит текущую выбранную строку. ‘1{p;q}’ — печатает 1 строку и выходит не читая остальные (p — печать, q — выход).

Ещё вариант чтения из файла (построчно):

while read line
do
    BTC=${line:0:13}
done </tmp/bitcoin.txt

А таким образом DISPLAY_LINE1=${LINE1:0:14} из строки LINE1 извлекаем подстроку длиной 14 символов начиная с 0.

Замена символов производится комбинацией //, например, так DOLLAR//–/-. Заменяется символ "–" на "-".

Файл parse.sh

файл parse.sh

#!/bin/bash
# Чтение данных погоды в формате RSS с сайта http://rp5.ru/rss/1859/ru
# 1859 - код города Брянск

# Функция конвертирования текстовой строки с валютой
conv(){
# Переворачиваем последовательность строк и читаем указанные строки
CURRENCY=$(sed -n '1!G;h;$p' /tmp/ex.xml | sed -n "${1}{p;q}")
CURRENCY=${CURRENCY//[^,^(^)^0-9^–^+]/}
echo $CURRENCY
}

# Сохраняем файлы c данными во временный каталог
# 1. Погода
wget -q -O /tmp/rp5weather.xml http://rp5.ru/rss/1859/ru

# 2. Получение курса доллара и евро
wget -q -O /tmp/ex.xml http://currr.ru/rss/

# 3. Получение данных bitcoin/ethereum
wget -q -O /tmp/bitcoin.json https://api.coinmarketcap.com/v1/ticker/bitcoin/
wget -q -O /tmp/ethereum.json https://api.coinmarketcap.com/v1/ticker/ethereum/

# Разбор погоды
# Читаем построчно файл, находим маркер, подготавливаем строки погоды
# и сохраняем в файл
LINE31=$(sed -n '31{p;q}' /tmp/rp5weather.xml)
LINE33=$(sed -n '33{p;q}' /tmp/rp5weather.xml)
WEATHER1=${LINE31//"</title>"}
WEATHER1=${WEATHER1//" °C"}
WEATHER1=${WEATHER1//" было "}
WEATHER1=${WEATHER1:29}
WEATHER2=${LINE33##*ожидается}
WEATHER2=${WEATHER2//"°"}

echo "${WEATHER1}С" > /tmp/weather.txt
echo ${WEATHER2%.*} >> /tmp/weather.txt

# Получаем значение Bitcoin
LINEBTC=$(sed -n '7{p;q}' /tmp/bitcoin.json)
echo "${LINEBTC//[^.^0-9]/}" > /tmp/bitcoin.txt

# Получаем значение Ethereum
LINEETH=$(sed -n '7{p;q}' /tmp/ethereum.json)
echo "${LINEETH//[^.^0-9]/}" > /tmp/ethereum.txt

# Разбор валюты
DOLLAR=$(conv 8)
echo $DOLLAR > /tmp/ex.txt
EURO=$(conv 6)
echo $EURO >> /tmp/ex.txt

Комментарии

Команда wget позволяет скачивать из сети файлы, страницы и т.д. Опция -q — выводит минимум информации, -O — сохраняет в указанный файл.

В строках ниже производится запись в файл:

echo "${WEATHER1}С" > /tmp/weather.txt
echo ${WEATHER2%.*} >> /tmp/weather.txt

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

Пример использования параметра в функции:

conv 6

Непосредственно в функции:

CURRENCY=$(sed -n '1!G;h;$p' /tmp/ex.xml | sed -n "${1}{p;q}")

Где {1} — параметр. Передаётся число 6.

Обратите внимание на сложную функцию замены подстроки, например:

LINEBTC//[^.^0-9]/

В строке остаются только символ "." и все цифры от 0 до 9.

Послесловие

В языке bash доступны практически все возможности "обычных" языков программирования. А некоторые команды, по сравнению с аналогами в 1С, удивляют своей лаконичностью и функциональностью.

На данный момент дисплей покупателя в качестве информационного табло стабильно работает больше полугода.

Список ресурсов

  1. Страница дисплея покупателя LPOS-VFD
  2. Программируем символы валют для дисплея покупателя
  3. Основы BASH (часть 1)
  4. Основы BASH (часть 2)
  5. Как пользоваться PuTTY
  6. Текстовый редактор nano в Linux для новичков
  7. Инструкция для пользователей WinSCP

Автор: stone78

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js