Опыт организации диалога с модулями ICP DAS I-7000 по RS-485 используя только bash

от автора

Предыстория:

Предыстория: после очередной оптимизации затрат в моем предприятии был прекращен контракт с организацией, которая предоставляла аналитические услуги по режимам работы оборудования и этот весь «нехорошее слово» перевели в задачу на системного администратора (все равно ему зарплату платим :)) и в перспективе аналитика. Как результат фирма отозвала лицензию и права на использования ПО, о чем предупредила письменно, а у меня на руках оказалось около 20 «черных ящиков» и пожелания руководства о том, что информацию все равно нужно получать в главный офис.

Дано:

Схема обычного тривиального подключения по RS-485
Схема обычного тривиального подключения по RS-485
  1. ICP DAS I-7080

  2. ICP DAS I-7019

  3. ICOP VDX2 на Vortex86 c RS-232, RS-485, USB2, VGA, PS/2 и промышленным накопителем на 4Gb

Проанализировав состояние накопителя, который уже потрудился 10 лет (установлена Windows XP, которая так и не стала нашей), было принято решение что следующей ОС будет Linux (дополнительная задача, это работа с модемом, отправка данных по стандартным протоколам и т.п.), тем более что она прекрасно работает с обычными флэш-накопителями и как оказалось ядро i586 совместимо с Vortex86. Но ложкой дегтя стала версия совместимого с платой дистрибутива Debian8, который уже можно причислять к ряду некро-, а также наличие всего 256 Мб ОЗУ на плате.
Ничто не предвещало проблем: было вывялено что rs485 расположен в /dev/ttyS1. Модуль I-7019 под адресом #1, I-7080 — #2, контрольная сумма не используется и документация на модули в сети есть. Через minicom модули отзывчиво давали результаты, единственное что это было не в классическом стиле листинга, а в одну строку. Сам minicom модифицирует настройки порта, которые можно увидеть командой
stty -a -F /dev/ttyS0

Тут для нас важным является отключение аппаратного управления потоком параметром
crtsdts.

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

stty 9600 min 1 time 10 -echo -crtscts ignbrk -brkint icrnl -imaxbel -opost -isig -icanon -iexten -echo echok -echoctl -echoke -F /dev/ttyS1

есть подозрение, что тут всё-таки есть неточность, из-за которой начались кривые отношения с модулями.

Стандартный вариант диалога с модулем по RS-485 в режиме команд:

#!/bin/bash TTY="/dev/ttyS1" SLEEP=6 RESULT=$(echo -e '#020\r' > $TTY; sleep $SLEEP; kill %cat) //счит. значений счетчика 0 i7080 echo $result

И нельзя сказать, что это работает, так как модуль может просто «забить» на команду, путем опытных испытаний было выявлена «отзывчивость» модулей: I-7019 около 95%, а вот I-7080 около 75%. Были проведены эксперименты с разными значениями SLEEP от 1 до 12, лучшие результаты на значениях 3, 6 и 7.

Так не бывает!? Может порт проблемный? Чтобы исключить проблему с портом, был использован самый простой свисток с AliExpress на контроллере HL-340, который проявил себя лучше встроенного, доведя значения «отзывчивости» для I-7019 около 97%, а для I-7080 до 90%.

Без боязни «запачкать код нечистотами» было принято решение скидывать опрос модуля в файл, а потом уже обрабатывать результаты как промежуточный вариант средствами того же bash, дабы результаты начальству все таки нужны в срок.

Пример части скрипта:

… fls1="/data/log.1"; #TTY="/dev/ttyS1" TTY="/dev/ttyUSB1" SLEEP4=4; #задержка RESULT=$(cat $TTY >> $fls1 & # ответы пишем в файл echo -e "\ncount0.1 "`date +"%H:%M:%S"`"\t*" >> $fls1; #пишем в файл время попытки echo -e '#020\r' > $TTY; sleep $SLEEP4; #первая попытка считать ответ echo -e "\ncount0.2 "`date +"%H:%M:%S"`"\t*" >> $fls1; #пишем в файл время попытки echo -e '#020\r' > $TTY; sleep $SLEEP4; #вторая попытка считать в ответ kill %cat) # принудительно завершаем диалог …

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

Кроме опроса счетчика его нужно обнулять, а это команда «$0260» для счетчика 0 и «$0261» для счетчика 1. Идея перенести опрос порта в отдельный файл и получать только строку с успешным результатом не покидала и поэтому код модифицировал до следующего:

#!/bin/bash s_cmd=$1 case $s_cmd in # для примера перечислим несколько команд data20) # получаем данные счетчика0 от модуля2 s_cmd ='#020\r' ;; reset20) # обнуляем данные счетчика0 от модуля2 s_cmd ='$0260\r' ;; data7019) # получаем данные АЦП от модуля1 s_cmd ='#01\r' ;; *) s_cmd ='$02M\r' //выводим информацию о имени модуля2 если ничего нет ;; esac #TTY="/dev/ttyS1" – со встроенным портом нет адекватной работы TTY="/dev/ttyUSB0" SLEEP=5 RESULT="" while [ ! -n "$RESULT" ]; do # пока модуль не ответит, «дергаем» его RESULT=$(cat $TTY & echo -e "$s_cmd " > $TTY; sleep $SLEEP; kill %cat) #обычно на первый нет ответа RESULT=$(cat $TTY & echo -e "$s_cmd " > $TTY; sleep $SLEEP; kill %cat) done echo "$RESULT" 

ну и вызов, собственно, по типу:
 ./rs485.bash data20

Не покидала мысль что существующий метод «кривой», и все-таки нужно сделать по-другому. Я стал пробовать другие варианты, и случаем, читая информацию про очередные жалобы о проблемах общения с последовательным портом набрел на обсуждение >тут<. Увидел другой вариант, и оказалось, что встроенный контроллер дал вменяемую устойчивость адекватных ответов, и моя часть кода диалога по rs485 приобрела вид:

#блок выбора команд в s_cmd из вышестоящего примера… …строки 1-16 вышестоящего примера… TTY="/dev/ttyS1" #порт rs485  fil="/tmp/ttyDump.dat" rm $fil  #удаляем результаты предыдущего ответа while [ ! -s $fil ]; do   # пока нет ответа модуля в файле exec 3<$TTY               #перенаправляем ответ порта в файловый дескриптор   cat <&3 > $fil &        #перенаправляем ответ порта из дескриптора в файл   PID=$!                  #получаем PID для последующего забоя CAT     echo -e $s_cmd > $TTY #отправляем команду в порт     sleep 7   kill $PID               #KILL наш CAT   wait $PID 2>/dev/null   #ждем завершения CAT exec 3<&-                 #освобождаем файловый дескриптор 3 done cat $fil                  #показываем полученные данные echo "" #делаем перенос строки для ответа скрипта

Возможно, мои мытарства кому-то позволят сэкономить время и облегчить труд bash программирования «на коленке» без использования сторонних библиотек.

По поводу советов что писать надо было сразу на C/C++ могу заметить, что львиная доля уже существующих обсуждений и вопросов по программированию rs485 на просторах сети, позволяют заподозрить что и там не все так гладко как хотелось бы, попадаются советы вплоть до перекомпиляции ядра. А эксперименты с Python учитывая устаревшую версию ОС сразу же закончились отсутвием поддержки современных библиотек. Что тут можно сказать? Будет время на эти задачи – попробуем (я не активный C/C++ программист). Пока существует масса других задач как по бэкенду, так и фронтенду.


ссылка на оригинал статьи https://habr.com/ru/post/598965/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *